mirror of https://github.com/tongzx/nt5src
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.
6981 lines
210 KiB
6981 lines
210 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
|
|
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.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "ntddk.h"
|
|
#include "ntddscsi.h"
|
|
#include "ntddcdrm.h"
|
|
#include "stdio.h"
|
|
#include "scsi.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
|
|
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
|
|
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
|
|
CdAudioHPCdrDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
HpCdrProcessLastSession(
|
|
IN PCDROM_TOC Toc
|
|
);
|
|
|
|
NTSTATUS
|
|
HPCdrCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioForwardIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID CdAudioUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
//
|
|
// 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 NEC and one of the other
|
|
// device brands, some amount of code is freed.
|
|
//
|
|
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#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
|
|
SendSrbSynchronous(
|
|
IN PCD_DEVICE_EXTENSION Extension,
|
|
IN PSCSI_PASS_THROUGH Srb,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the given SRB synchronously to the CDROM class driver.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Srb - Supplies the SRB.
|
|
|
|
Buffer - Supplies the return buffer.
|
|
|
|
BufferLength - Supplies the buffer length.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ioctl;
|
|
KEVENT event;
|
|
PIRP irp = NULL;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
Srb->Length = sizeof(SCSI_PASS_THROUGH);
|
|
Srb->SenseInfoLength = 0;
|
|
Srb->SenseInfoOffset = 0;
|
|
|
|
if (Buffer) {
|
|
Srb->DataIn = SCSI_IOCTL_DATA_IN;
|
|
Srb->DataTransferLength = BufferLength;
|
|
Srb->DataBufferOffset = (ULONG_PTR) Buffer;
|
|
ioctl = IOCTL_SCSI_PASS_THROUGH_DIRECT;
|
|
} else {
|
|
Srb->DataIn = SCSI_IOCTL_DATA_OUT;
|
|
Srb->DataTransferLength = 0;
|
|
Srb->DataBufferOffset = 0;
|
|
ioctl = IOCTL_SCSI_PASS_THROUGH;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(ioctl, Extension->TargetDeviceObject,
|
|
Srb, sizeof(SCSI_PASS_THROUGH),
|
|
Srb, sizeof(SCSI_PASS_THROUGH),
|
|
FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(Extension->TargetDeviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates and initializes a new FDO for the corresponding
|
|
PDO. It may perform property queries on the FDO but cannot do any
|
|
media access operations.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - CDROM class driver object.
|
|
|
|
Pdo - the physical device object we are being added to
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCD_DEVICE_EXTENSION extension;
|
|
ULONG regActive = CDAUDIO_SEARCH_ACTIVE;
|
|
|
|
//
|
|
// Use registry to potentially not load onto stack
|
|
//
|
|
|
|
{
|
|
HANDLE deviceParameterHandle;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
//
|
|
// See if key exists and is readable.
|
|
//
|
|
|
|
status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
KEY_READ,
|
|
&deviceParameterHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Pnp keys should always exist and be system-readable
|
|
//
|
|
|
|
CdDump((0, "AddDevice !! Registry key DNE?! %lx\n", status));
|
|
|
|
ASSERT(FALSE);
|
|
|
|
regActive = CDAUDIO_SEARCH_ACTIVE;
|
|
goto AddDeviceEndRegistry;
|
|
}
|
|
|
|
//
|
|
// Zero out the memory
|
|
//
|
|
|
|
RtlZeroMemory(&queryTable, sizeof(queryTable));
|
|
|
|
//
|
|
// Setup the structure for the read call
|
|
//
|
|
|
|
queryTable->Flags =
|
|
RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable->Name = CDAUDIO_ACTIVE_KEY_NAME;
|
|
queryTable->EntryContext = ®Active;
|
|
queryTable->DefaultType = REG_DWORD;
|
|
queryTable->DefaultData = NULL;
|
|
queryTable->DefaultLength = 0;
|
|
|
|
//
|
|
// Get the value in regActive (using queryTable)
|
|
//
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)deviceParameterHandle,
|
|
queryTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Check for failure...
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This is normal, as the key does not exist the first
|
|
// time the system loads the driver for the device.
|
|
//
|
|
|
|
CdDump(( 2,
|
|
"AddDevice !! Read value, status %lx\n",
|
|
status));
|
|
regActive = CDAUDIO_SEARCH_ACTIVE;
|
|
|
|
} else if (regActive > CDAUDIO_MAX_ACTIVE) {
|
|
|
|
//
|
|
// The registry value has either been corrupted, or manually
|
|
// set to CDAUDIO_SEARCH_ACTIVE. Either way, the driver will
|
|
// search for drive type later.
|
|
//
|
|
|
|
CdDump(( 2,
|
|
"AddDevice !! Need to search, value %x\n",
|
|
regActive));
|
|
regActive = CDAUDIO_SEARCH_ACTIVE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We read a valid value, which will override the mapping type.
|
|
//
|
|
|
|
CdDump(( 2,
|
|
"AddDevice => Read value %x\n",
|
|
regActive));
|
|
|
|
}
|
|
|
|
//
|
|
// close the handle
|
|
//
|
|
|
|
ZwClose(deviceParameterHandle);
|
|
|
|
} // Finished registry handling
|
|
|
|
AddDeviceEndRegistry:
|
|
|
|
//
|
|
// We forcibly set to within these bounds above
|
|
//
|
|
|
|
if (( regActive > CDAUDIO_MAX_ACTIVE ) &&
|
|
( regActive != CDAUDIO_SEARCH_ACTIVE )) {
|
|
CdDump((0,
|
|
"AddDevice => Invalid registry value for "
|
|
"maptype %x, resetting\n",
|
|
regActive
|
|
));
|
|
regActive = CDAUDIO_SEARCH_ACTIVE;
|
|
}
|
|
|
|
|
|
|
|
CdDump((1,
|
|
"AddDevice => Active == %x\n",
|
|
regActive));
|
|
|
|
//
|
|
// The system will remove us from memory if we don't call IoCreateDevice
|
|
//
|
|
|
|
if (regActive == CDAUDIO_NOT_ACTIVE) {
|
|
CdDump((2,
|
|
"AddDevice => Not attaching for pdo %p\n",
|
|
PhysicalDeviceObject
|
|
));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Map support section into non-paged pool
|
|
//
|
|
|
|
switch (regActive) {
|
|
|
|
case CDAUDIO_NEC:
|
|
MmLockPagableCodeSection((PVOID)CdAudioNECDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_PIONEER:
|
|
case CDAUDIO_PIONEER624:
|
|
MmLockPagableCodeSection((PVOID)CdAudioPioneerDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_DENON:
|
|
MmLockPagableCodeSection((PVOID)CdAudioDenonDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_HITACHI:
|
|
case CDAUDIO_FUJITSU:
|
|
MmLockPagableCodeSection((PVOID)CdAudioHitachiDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_CDS535:
|
|
MmLockPagableCodeSection((PVOID)CdAudio535DeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_CDS435:
|
|
MmLockPagableCodeSection((PVOID)CdAudio435DeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_ATAPI:
|
|
MmLockPagableCodeSection((PVOID)CdAudioAtapiDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_HPCDR:
|
|
MmLockPagableCodeSection((PVOID)CdAudioHPCdrDeviceControl);
|
|
break;
|
|
|
|
case CDAUDIO_SEARCH_ACTIVE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create the devObj so we are used
|
|
//
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(CD_DEVICE_EXTENSION),
|
|
NULL,
|
|
PhysicalDeviceObject->DeviceType,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&deviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 0,
|
|
"AddDevice !! Unable to create device %lx\n",
|
|
status
|
|
));
|
|
|
|
// LOGLOG
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set device object flags, device extension
|
|
//
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
if (deviceObject->Flags & DO_POWER_INRUSH) {
|
|
CdDump((0,
|
|
"AddDevice ?? DO_POWER_INRUSH set for DO %p\n",
|
|
deviceObject
|
|
));
|
|
} else {
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
|
|
extension = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(extension, sizeof(CD_DEVICE_EXTENSION));
|
|
|
|
//
|
|
// Useful to have next lower driver
|
|
//
|
|
|
|
extension->TargetDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
|
|
if (!extension->TargetDeviceObject) {
|
|
|
|
CdDump(( 0,
|
|
"AddDevice !! Unable to attach to device stack %lx\n",
|
|
STATUS_NO_SUCH_DEVICE
|
|
));
|
|
|
|
// LOGLOG
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
KeInitializeEvent(&extension->PagingPathCountEvent, SynchronizationEvent, TRUE);
|
|
|
|
//
|
|
// Must set Active flag, Pdo
|
|
//
|
|
|
|
extension->Active = (UCHAR)regActive;
|
|
extension->DeviceObject = deviceObject;
|
|
extension->TargetPdo = PhysicalDeviceObject;
|
|
|
|
//
|
|
// No longer initializing
|
|
//
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioSignalCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PKEVENT Event
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This completion routine will signal the event given as context and then
|
|
return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
|
|
the responsibility of the routine waiting on the event to complete the
|
|
request and free the event.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object
|
|
|
|
Irp - a pointer to the irp
|
|
|
|
Event - a pointer to the event to signal
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for START DEVICE.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
|
|
#if DBG
|
|
UCHAR string[17];
|
|
#endif
|
|
|
|
CdDump((2, "StartDevice => Entering.\n"));
|
|
|
|
status = CdAudioForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
// LOGLOG - Should put some message into the system log
|
|
|
|
return status;
|
|
}
|
|
|
|
///
|
|
/// From this point forward, not matter what occurs, we should
|
|
/// return STATUS_SUCCESS. The rest of the code is non-critical,
|
|
/// and the worst occurance is that audio will not play on a CDROM.
|
|
///
|
|
|
|
CdDump((2, "StartDevice => Starting\n"));
|
|
|
|
//
|
|
// Initialize 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;
|
|
|
|
//
|
|
// deviceExtension->Active possibly set from registry in AddDevice
|
|
//
|
|
|
|
ASSERT(deviceExtension->Active > 0);
|
|
ASSERT((deviceExtension->Active <= CDAUDIO_MAX_ACTIVE) ||
|
|
(deviceExtension->Active == CDAUDIO_SEARCH_ACTIVE));
|
|
|
|
//
|
|
// Search for the type of translation via the inquiry data
|
|
// if registry value DNE or says to. Otherwise, use the
|
|
// registry value (gotten in CdAudioAddDevice) as the Active Value
|
|
//
|
|
|
|
if (deviceExtension->Active == (UCHAR)CDAUDIO_SEARCH_ACTIVE) {
|
|
|
|
SCSI_PASS_THROUGH srb;
|
|
PCDB cdb = (PCDB) srb.Cdb;
|
|
PUCHAR inquiryDataPtr = NULL;
|
|
UCHAR attempt = 0;
|
|
|
|
CdDump(( 1,
|
|
"StartDevice => Searching for map type via InquiryData\n"
|
|
));
|
|
|
|
//
|
|
// Allocate buffer for returned inquiry data
|
|
//
|
|
|
|
inquiryDataPtr = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
INQUIRYDATABUFFERSIZE
|
|
);
|
|
if (!inquiryDataPtr) {
|
|
CdDump(( 0,
|
|
"StartDevice !! Insufficient resources for inquiry data\n"
|
|
));
|
|
deviceExtension->Active = CDAUDIO_NOT_ACTIVE;
|
|
// LOGLOG
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Force it into the loop
|
|
//
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
CdDump(( 4,
|
|
"StartDevice => Inquiry Data at %p\n",
|
|
inquiryDataPtr
|
|
));
|
|
|
|
//
|
|
// Try to get inquiry data a few times
|
|
//
|
|
while (
|
|
!(NT_SUCCESS(status)) &&
|
|
(attempt++ < MAXIMUM_RETRIES)
|
|
) {
|
|
CdDump(( 1,
|
|
"StartDevice => Inquiry attempt %d\n",
|
|
attempt
|
|
));
|
|
|
|
//
|
|
// Zero SRB (including cdb)
|
|
//
|
|
|
|
RtlZeroMemory( &srb, sizeof(SCSI_PASS_THROUGH) );
|
|
|
|
//
|
|
// Just for safety, zero the inquiryDataPtr
|
|
//
|
|
|
|
RtlZeroMemory( inquiryDataPtr, INQUIRYDATABUFFERSIZE );
|
|
|
|
//
|
|
// Fill in CDB for INQUIRY to CDROM
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
|
cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
|
|
|
|
|
|
//
|
|
// Inquiry length is 6, with timeout
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
inquiryDataPtr,
|
|
INQUIRYDATABUFFERSIZE
|
|
);
|
|
CdDump(( 2,
|
|
"StartDevice => Inquiry status for attempt %d is %lx\n",
|
|
attempt,
|
|
status
|
|
));
|
|
}
|
|
|
|
//
|
|
// So if it failed a bunch of times....
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1, "StartDevice !! Inquiry failed! %lx\n", status ));
|
|
ExFreePool( inquiryDataPtr );
|
|
|
|
// LOGLOG
|
|
|
|
//
|
|
// Do not translate any commands if we cannot determine
|
|
// the drive type. Better to lose audio than to lose
|
|
// data functionality.
|
|
//
|
|
|
|
deviceExtension->Active = CDAUDIO_NOT_ACTIVE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#if DBG
|
|
RtlZeroMemory( string, 17 );
|
|
RtlCopyMemory( string, &(inquiryDataPtr[8]), 8 );
|
|
CdDump((2, "StartDevice => Vendor '%s'\n", string));
|
|
RtlZeroMemory( string, 17 );
|
|
RtlCopyMemory( string, &(inquiryDataPtr[16]), 16 );
|
|
CdDump((2, "StartDevice => Drive '%s'\n", string));
|
|
#endif
|
|
|
|
|
|
//
|
|
// Conduct a search by the inquiry data
|
|
//
|
|
|
|
{
|
|
//
|
|
// Set the default value to NONE (not SEARCH_ACTIVE)
|
|
//
|
|
|
|
deviceExtension->Active = CDAUDIO_NOT_ACTIVE;
|
|
|
|
//
|
|
// Check for NEC drive
|
|
//
|
|
|
|
if ( RtlEqualMemory( &(inquiryDataPtr[8]), "NEC ", 8 )) {
|
|
if (NecSupportNeeded(inquiryDataPtr)) {
|
|
MmLockPagableCodeSection((PVOID)CdAudioNECDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_NEC;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for PIONEER DRM-600 and DRM-600x drives
|
|
//
|
|
|
|
if ( (RtlEqualMemory( &(inquiryDataPtr[8]), "PIONEER ", 8 )) &&
|
|
(RtlEqualMemory( &(inquiryDataPtr[16]), "CD-ROM DRM-600", 15 ))
|
|
) {
|
|
MmLockPagableCodeSection((PVOID)CdAudioPioneerDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_PIONEER;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if ( RtlEqualMemory( &(inquiryDataPtr[8]), "CHINON", 6 )) {
|
|
|
|
//
|
|
// Check for Chinon CDS-535
|
|
//
|
|
|
|
if ((inquiryDataPtr[27]=='5') &&
|
|
(inquiryDataPtr[28]=='3') &&
|
|
(inquiryDataPtr[29]=='5') &&
|
|
(inquiryDataPtr[32]=='Q')
|
|
) {
|
|
MmLockPagableCodeSection((PVOID)CdAudio535DeviceControl);
|
|
deviceExtension->Active = CDAUDIO_CDS535;
|
|
}
|
|
|
|
//
|
|
// Check for Chinon CDS-435 or CDS-431
|
|
// (willing to handle versions M/N, S/U, and H)
|
|
//
|
|
|
|
if ((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;
|
|
}
|
|
|
|
//
|
|
// End of the Chinon drives
|
|
//
|
|
}
|
|
|
|
|
|
//
|
|
// Check for HITACHI drives
|
|
//
|
|
|
|
if ( (RtlEqualMemory( &(inquiryDataPtr[8]), "HITACHI ", 8 )) &&
|
|
( (RtlEqualMemory( &(inquiryDataPtr[16]), "CDR-3650/1650S ", 16 )) ||
|
|
(RtlEqualMemory( &(inquiryDataPtr[16]), "CDR-1750S ", 16 ))
|
|
)
|
|
) {
|
|
MmLockPagableCodeSection((PVOID)CdAudioHitachiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_HITACHI;
|
|
}
|
|
|
|
//
|
|
// Check for Atapi drives that require support.
|
|
//
|
|
|
|
if ( ((RtlEqualMemory( &(inquiryDataPtr[8]), "WEARNES ", 8 )) &&
|
|
(RtlEqualMemory( &(inquiryDataPtr[16]), "RUB", 3 ))
|
|
) ||
|
|
((RtlEqualMemory( &(inquiryDataPtr[8]), "OTI ", 8)) &&
|
|
(RtlEqualMemory( &(inquiryDataPtr[16]), "DOLPHIN ", 8))
|
|
)
|
|
) {
|
|
MmLockPagableCodeSection((PVOID)CdAudioAtapiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_ATAPI;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
}
|
|
|
|
//
|
|
// Check for FUJITSU drives
|
|
//
|
|
|
|
if (RtlEqualMemory( &(inquiryDataPtr[8]), "FUJITSU ", 8 )) {
|
|
|
|
//
|
|
// 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;
|
|
|
|
} 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;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for HP CDR
|
|
//
|
|
|
|
if ((RtlEqualMemory( &(inquiryDataPtr[8]), "HP ", 8 )) &&
|
|
(RtlEqualMemory( &(inquiryDataPtr[16]), "C4324/C4325", 11 ))
|
|
) {
|
|
MmLockPagableCodeSection((PVOID)CdAudioHPCdrDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_HPCDR;
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool( inquiryDataPtr );
|
|
}
|
|
|
|
CdDump((2,
|
|
"StartDevice => Active is set to %x\n",
|
|
deviceExtension->Active));
|
|
|
|
//
|
|
// Store the value in the registry so the inquiry data does
|
|
// not have to be read and searched.
|
|
//
|
|
{
|
|
HANDLE deviceParameterHandle;
|
|
ULONG keyValue = (ULONG)deviceExtension->Active;
|
|
|
|
//
|
|
// Open a handle to the key
|
|
//
|
|
|
|
status = IoOpenDeviceRegistryKey(deviceExtension->TargetPdo,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
KEY_WRITE,
|
|
&deviceParameterHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
CdDump(( 0,
|
|
"StartDevice !! Failed to open registry %lx\n",
|
|
status
|
|
));
|
|
|
|
// LOGLOG
|
|
|
|
//
|
|
// Handle not open, so just
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Write the value
|
|
//
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
|
|
(PWSTR) deviceParameterHandle,
|
|
CDAUDIO_ACTIVE_KEY_NAME,
|
|
REG_DWORD,
|
|
&keyValue,
|
|
sizeof(keyValue));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This is a non-fatal error, so just write to debugger?
|
|
//
|
|
|
|
CdDump(( 0,
|
|
"StartDevice !! Failed to write registry %lx\n",
|
|
status
|
|
));
|
|
// LOGLOG
|
|
|
|
//
|
|
// But fall through to close the handle to the registry
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// Don't forget to close what we open...
|
|
//
|
|
|
|
ZwClose(deviceParameterHandle);
|
|
CdDump(( 2,
|
|
"StartDevice => Wrote value %x successfully\n",
|
|
deviceExtension->Active
|
|
));
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for PNP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
|
|
switch (irpSp->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE: {
|
|
status = CdAudioStartDevice(DeviceObject, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
|
|
ULONG count;
|
|
BOOLEAN setPagable;
|
|
PCD_DEVICE_EXTENSION deviceExtension;
|
|
|
|
|
|
if (irpSp->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
|
|
return CdAudioSendToNextDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// wait on the paging path event
|
|
//
|
|
|
|
status = KeWaitForSingleObject(&deviceExtension->PagingPathCountEvent,
|
|
Executive, KernelMode,
|
|
FALSE, NULL);
|
|
|
|
//
|
|
// if removing last paging device, need to clear DO_POWER_PAGABLE
|
|
// bit here, and possible re-set it below on failure.
|
|
//
|
|
|
|
setPagable = FALSE;
|
|
if (!irpSp->Parameters.UsageNotification.InPath &&
|
|
deviceExtension->PagingPathCount == 1 ) {
|
|
|
|
//
|
|
// removing the last paging file
|
|
// must have DO_POWER_PAGABLE bits set
|
|
//
|
|
|
|
if (DeviceObject->Flags & DO_POWER_INRUSH) {
|
|
CdDump((2, "Pnp: Last paging file removed "
|
|
"but DO_POWER_INRUSH set, so not setting PAGABLE bit "
|
|
"for DO %p\n", DeviceObject));
|
|
} else {
|
|
CdDump((2, "Pnp: Setting PAGABLE bit "
|
|
"for DO %p\n", DeviceObject));
|
|
DeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
setPagable = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// send the irp synchronously
|
|
//
|
|
|
|
status = CdAudioForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
//
|
|
// now deal with the failure and success cases.
|
|
// note that we are not allowed to fail the irp
|
|
// once it is sent to the lower drivers.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IoAdjustPagingPathCount(
|
|
&deviceExtension->PagingPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath);
|
|
|
|
if (irpSp->Parameters.UsageNotification.InPath) {
|
|
|
|
if (deviceExtension->PagingPathCount == 1) {
|
|
CdDump((2, "Pnp: Clearing PAGABLE bit "
|
|
"for DO %p\n", DeviceObject));
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
}
|
|
|
|
} // end InPath if/else
|
|
|
|
} else {
|
|
|
|
//
|
|
// cleanup the changes done above
|
|
//
|
|
|
|
if (setPagable == TRUE) {
|
|
CdDump((2, "Pnp: Un-setting pagable bit for DO %p\n", DeviceObject));
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
setPagable = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// set the event so the next one can occur.
|
|
//
|
|
|
|
KeSetEvent(&deviceExtension->PagingPathCountEvent,
|
|
IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// and complete the irp
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
return CdAudioSendToNextDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Send everything down unless specifically handled.
|
|
//
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
DriverObject->MajorFunction[i] = CdAudioSendToNextDriver;
|
|
}
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = CdAudioReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = CdAudioReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CdAudioDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = CdAudioPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = CdAudioPower;
|
|
DriverObject->DriverExtension->AddDevice = CdAudioAddDevice;
|
|
DriverObject->DriverUnload = CdAudioUnload;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#define NEC_CDAUDIO_SUPPORT_DRIVES 12
|
|
|
|
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 badDriveList[NEC_CDAUDIO_SUPPORT_DRIVES] = {
|
|
"CD-ROM DRIVE:80 ", // must be 16 byte long
|
|
"CD-ROM DRIVE:82 ",
|
|
"CD-ROM DRIVE:83 ",
|
|
"CD-ROM DRIVE:84 ",
|
|
"CD-ROM DRIVE:841",
|
|
"CD-ROM DRIVE:38 ",
|
|
"CD-ROM DRIVE 4 M",
|
|
"CD-ROM DRIVE:500",
|
|
"CD-ROM DRIVE:400",
|
|
"CD-ROM DRIVE:401",
|
|
"CD-ROM DRIVE:501",
|
|
"CD-ROM DRIVE:900"};
|
|
|
|
|
|
for (i = 0; i < NEC_CDAUDIO_SUPPORT_DRIVES; i++) {
|
|
if (RtlCompareMemory(inquiryData->ProductId, badDriveList[i], 16)==16) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
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_SEARCH_ACTIVE:
|
|
|
|
//
|
|
// This occurs while we have not finished StartDevice()
|
|
//
|
|
|
|
status = CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_NOT_ACTIVE:
|
|
CdDump(( 3,
|
|
"DeviceControl => 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_ATAPI:
|
|
status = CdAudioAtapiDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_HPCDR:
|
|
status = CdAudioHPCdrDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
default:
|
|
|
|
// LOGLOG
|
|
|
|
CdDump(( 0,
|
|
"DeviceControl !! Active==UNKNOWN %x\n",
|
|
deviceExtension->Active
|
|
));
|
|
ASSERT(FALSE);
|
|
deviceExtension->Active = CDAUDIO_NOT_ACTIVE;
|
|
status = CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
}
|
|
|
|
|
|
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, Executive, 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
|
|
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_PASS_THROUGH 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,
|
|
"NECDeviceControl => IOCTL_CDROM_GET_LAST_SESSION recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Ensure we have a large enough buffer?
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[1])) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
Irp->IoStatus.Information = 0;
|
|
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;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
NEC_CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"NECDeviceControl => READ_TOC error, status %lx\n",
|
|
status ));
|
|
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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 - 2) >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)((bytesTransfered - 2) & 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,
|
|
"NECDeviceControl => Tracks %d - %d, (%x 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,
|
|
"NECDeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
NEC_CDROM_TOC_SIZE
|
|
);
|
|
|
|
if ( Toc == NULL ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump(( 4,
|
|
"NECDeviceControl => Toc = %p cdaudioDataOut = %p\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 = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
NEC_CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"NECDeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
|
|
if (status != STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "NECDeviceControl => SRB ERROR (%lx)\n",
|
|
status ));
|
|
Irp->IoStatus.Information = 0;
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into our format.
|
|
//
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength >
|
|
sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[9]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[19]);
|
|
|
|
CdDump(( 4,
|
|
"NECDeviceControl => Tracks %d - %d, (%x bytes)\n",
|
|
cdaudioDataOut->FirstTrack,
|
|
cdaudioDataOut->LastTrack,
|
|
bytesTransfered
|
|
));
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
ULONG dataLength;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
|
|
dataLength = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[tracksOnCd])) - 2;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(dataLength >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(dataLength & 0xFF);
|
|
|
|
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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,
|
|
"NECDeviceControl => 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]
|
|
));
|
|
i++;
|
|
}
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// 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,
|
|
"NECDeviceControl => IOCTL_CDROM_PLAY_AUDIO_MSF recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// 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,
|
|
"NECDeviceControl => 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 = SendSrbSynchronous(deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
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,
|
|
"NECDeviceControl => 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 = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"NECDeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// 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,
|
|
"NECDeviceControl => seek MSF is %d:%d:%d\n",
|
|
cdb->NEC_SEEK_AUDIO.Minute,
|
|
cdb->NEC_SEEK_AUDIO.Second,
|
|
cdb->NEC_SEEK_AUDIO.Frame
|
|
));
|
|
|
|
status = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"NECDeviceControl => IOCTL_CDROM_PAUSE_AUDIO recv'd.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_PAUSE_AUDIO.OperationCode = NEC_STILL_CODE;
|
|
status = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"NECDeviceControl => IOCTL_CDROM_RESUME_AUDIO recv'd.\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 = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
NEC_Q_CHANNEL_TRANSFER_SIZE
|
|
);
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
|
|
CdDump(( 5,
|
|
"NECDeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"NECDeviceControl !! READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
RtlZeroMemory( SubQPtr, NEC_Q_CHANNEL_TRANSFER_SIZE );
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump((1,
|
|
"NECDeviceControl !! READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
Irp->IoStatus.Information = 0;
|
|
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;
|
|
// Transfer Length
|
|
cdb->NEC_READ_Q_CHANNEL.TransferSize = NEC_Q_CHANNEL_TRANSFER_SIZE;
|
|
CdDump(( 4, "NECDeviceControl => cdb = %p srb = %p SubQPtr = %p\n",
|
|
cdb,
|
|
&srb,
|
|
SubQPtr
|
|
));
|
|
status = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
NEC_Q_CHANNEL_TRANSFER_SIZE
|
|
);
|
|
CdDump(( 4, "NECDeviceControl => READ_Q_CHANNEL, status is %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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
CdDump(( 5,
|
|
"NECDeviceControl => <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,
|
|
"NECDeviceControl => <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 * (LONGLONG)1000 * 100 * 5;
|
|
|
|
//
|
|
// Stall for a while to let the controller spinup.
|
|
//
|
|
|
|
KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&delay);
|
|
|
|
if (retryCount++ < MAXIMUM_RETRIES) {
|
|
goto NECSeek;
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump((1,
|
|
"NECDeviceControl => READ_Q_CHANNEL failed %lx)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
CdDump(( 3,
|
|
"NECDeviceControl => IOCTL_CDROM_EJECT_MEDIA recv'd.\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 = SendSrbSynchronous(
|
|
deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump(( 3,
|
|
"NECDeviceControl => invalidating cached TOC!\n"
|
|
));
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
|
|
CdDump(( 3, "NECDeviceControl => Not Supported yet.\n" ));
|
|
Irp->IoStatus.Information = 0;
|
|
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(( 10,
|
|
"NECDeviceControl => Unsupported device IOCTL (%x)\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_PASS_THROUGH 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,
|
|
"PioneerDeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => Start Unit failed (%lx)\n",
|
|
status));
|
|
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => ReadTOC, First/Last Tracks failed (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[0]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[1]);
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
ULONG dataLength;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
|
|
//
|
|
// set the number of tracks correctly
|
|
//
|
|
|
|
dataLength = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[tracksOnCd])) - 2;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(dataLength >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(dataLength & 0xFF);
|
|
|
|
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; i++ ) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
cdb->PNR_READ_TOC.OperationCode = PIONEER_READ_TOC_CODE;
|
|
// track
|
|
cdb->PNR_READ_TOC.TrackNumber =
|
|
(UCHAR)(DEC_TO_BCD((i+cdaudioDataOut->FirstTrack)));
|
|
cdb->PNR_READ_TOC.AssignedLength[1] = PIONEER_TRANSFER_SIZE;
|
|
cdb->PNR_READ_TOC.Type = PIONEER_READ_TRACK_INFO;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => ReadTOC, Track #%d, failed (%lx)\n",
|
|
i+cdaudioDataOut->FirstTrack,
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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]);
|
|
|
|
CdDump(( 4,
|
|
"CdAudioPioneerDeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => ReadTOC, read LeadOutTrack failed (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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]);
|
|
|
|
CdDump(( 4,
|
|
"CdAudioPioneerDeviceControl: 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]
|
|
));
|
|
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Set size of information transfered to
|
|
// max size possible for CDROM Table of Contents
|
|
//
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
ExFreePool( Toc );
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_STOP_AUDIO: {
|
|
CdDump((3,
|
|
"PioneerDeviceControl => IOCTL_CDROM_STOP_AUDIO recv'd.\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,
|
|
"PioneerDeviceControl => IOCTL_CDROM_PLAY_AUDIO_MSF recv'd.\n"
|
|
));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
} 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
} 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,
|
|
"PioneerDeviceControl => PLAY_AUDIO_MSF(stop) failed %lx\n",
|
|
status
|
|
));
|
|
|
|
CdDump(( 3,
|
|
"PioneerDeviceControl => cdb = %p, srb = %p\n",
|
|
cdb, &srb
|
|
));
|
|
|
|
if (CdAudioDebug>2)
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
}
|
|
#if DBG
|
|
else {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => PLAY_AUDIO_MSF(start) failed %lx\n",
|
|
status
|
|
));
|
|
CdDump(( 3,
|
|
"PioneerDeviceControl => cdb = %p, srb = %p\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,
|
|
"PioneerDeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\n"
|
|
));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
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,
|
|
"PioneerDeviceControl => 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,
|
|
"PioneerDeviceControl => Seek to MSF, cdb is %p, srb is %p\n",
|
|
cdb,
|
|
&srb
|
|
));
|
|
#if DBG
|
|
if (CdAudioDebug>2) {
|
|
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
#endif
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
} while ( !NT_SUCCESS(status) && ((retry--)>0) );
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump((1,
|
|
"PioneerDeviceControl => Seek to MSF failed %lx\n",
|
|
status
|
|
));
|
|
|
|
if (CdAudioDebug>5) {
|
|
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO: {
|
|
|
|
CdDump(( 3,
|
|
"PioneerDeviceControl => IOCTL_CDROM_PAUSE_AUDIO recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO: {
|
|
|
|
CdDump(( 3,
|
|
"PioneerDeviceControl => IOCTL_CDROM_RESUME_AUDIO recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Resume Play
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_PAUSE_AUDIO.OperationCode = PIONEER_PAUSE_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
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,
|
|
"PioneerDeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => READ_Q_CHANNEL, Illegal Format (%d)!\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
6
|
|
);
|
|
} 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,
|
|
"PioneerDeviceControl => read status code (Q) failed (%lx)\n",
|
|
status
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
PIONEER_Q_CHANNEL_TRANSFER_SIZE
|
|
);
|
|
|
|
} 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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump(( 1,
|
|
"PioneerDeviceControl => read q channel failed (%lx)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA: {
|
|
|
|
CdDump(( 3, "PioneerDeviceControl => "
|
|
"IOCTL_CDROM_EJECT_MEDIA recv'd. "));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
CdDump(( 1, "PioneerDeviceControl => "
|
|
"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, "PioneerDeviceControl => Not Supported yet.\n" ));
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((10,"PioneerDeviceControl => 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_PASS_THROUGH 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;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
if (status != STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "DenonDeviceControl => SRB ERROR (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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.DeviceIoControl.OutputBufferLength >
|
|
srb.DataTransferLength ?
|
|
srb.DataTransferLength :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[1]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[5]);
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
ULONG dataLength;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
|
|
//
|
|
// set the length of the data per SCSI2 spec
|
|
//
|
|
|
|
dataLength = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[tracksOnCd])) - 2;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(dataLength >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(dataLength & 0xFF);
|
|
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; 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]));
|
|
|
|
CdDump(( 4,
|
|
"CdAudioDenonDeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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]);
|
|
CdDump(( 4,
|
|
"CdAudioDenonDeviceControl: 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]
|
|
));
|
|
i++;
|
|
}
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_STOP_AUDIO_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"DenonDeviceControl => STOP failed (%lx)\n",
|
|
status ));
|
|
|
|
}
|
|
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_CDROM_STOP_AUDIO
|
|
) {
|
|
|
|
CdDump((3,
|
|
"DenonDeviceControl => IOCTL_CDROM_STOP_AUDIO recv'd.\n"
|
|
));
|
|
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump((3,
|
|
"DenonDeviceControl => IOCTL_CDROM_PLAY_AUDIO_MSF recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"DenonDeviceControl => PLAY ==> BcdLastEnd set to (%x %x %x)\n",
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => PLAY failed (%lx)\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;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"DenonDeviceControl => 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,
|
|
"DenonDeviceControl => SEEK failed (%lx)\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
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => IOCTL_CDROM_PAUSE_AUDIO recv'd.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
10
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => Pause, Read Q Channel failed (%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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => PAUSE, StopAudio failed! (%lx)\n",
|
|
status ));
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = SubQPtr[7];
|
|
deviceExtension->PausedS = SubQPtr[8];
|
|
deviceExtension->PausedF = SubQPtr[9];
|
|
|
|
CdDump((3,
|
|
"DenonDeviceControl => 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,
|
|
"DenonDeviceControl => IOCTL_CDROM_RESUME_AUDIO recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
} else {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => RESUME (%x %x %x) - (%x %x %x) failed (%lx)\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,
|
|
"DenonDeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => 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;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
10
|
|
);
|
|
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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
|
|
} else {
|
|
CdDump(( 1,
|
|
"DenonDeviceControl => READ_Q_CHANNEL failed (%lx)\n",
|
|
status
|
|
));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
CdDump(( 3,
|
|
"DenonDeviceControl => IOCTL_CDROM_EJECT_MEDIA recv'd.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_EJECT_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
CdDump(( 3, "DenonDeviceControl => Not Supported yet.\n" ));
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((10,"DenonDeviceControl => 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_PASS_THROUGH srb;
|
|
PHITACHICDB cdb = (PHITACHICDB)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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
PausePos,
|
|
3
|
|
);
|
|
|
|
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_PASS_THROUGH srb;
|
|
PHITACHICDB cdb = (PHITACHICDB)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,
|
|
"HitachiDeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"HitachiDeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
|
|
if (status != STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "HitachiDeviceControl => SRB ERROR (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
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.DeviceIoControl.OutputBufferLength >
|
|
sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->FirstTrack = Toc[2];
|
|
cdaudioDataOut->LastTrack = Toc[3];
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
ULONG dataLength;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
|
|
|
|
dataLength = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[tracksOnCd])) - 2;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(dataLength >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(dataLength & 0xFF);
|
|
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
for( i=0; i < tracksToReturn; 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];
|
|
|
|
CdDump(( 4,
|
|
"CdAudioHitachiDeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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];
|
|
CdDump(( 4,
|
|
"CdAudioHitachiDeviceControl: 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]
|
|
));
|
|
i++;
|
|
}
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
//
|
|
// 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.DeviceIoControl.OutputBufferLength >
|
|
sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->FirstTrack = Toc[1];
|
|
cdaudioDataOut->LastTrack = Toc[2];
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
ULONG dataLength;
|
|
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
|
|
dataLength = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[tracksOnCd])) - 2;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(dataLength >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(dataLength & 0xFF);
|
|
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; 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];
|
|
|
|
CdDump(( 4,
|
|
"CdAudioHitachiDeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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];
|
|
CdDump(( 4,
|
|
"CdAudioHitachiDeviceControl: 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]
|
|
));
|
|
i++;
|
|
}
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// 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;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
CdDump(( 3,
|
|
"HitachiDeviceControl => IOCTL_CDROM_PLAY_AUDIO_MSF recv'd.\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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"HitachiDeviceControl => PLAY failed (%lx)\n",
|
|
status ));
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
|
|
CdDump(( 3,
|
|
"HitachiDeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
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,
|
|
"HitachiDeviceControl => SEEK failed (%lx)\n",
|
|
status ));
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
{
|
|
|
|
PUCHAR PausePos = ExAllocatePool( NonPagedPoolCacheAligned, 3 );
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (PausePos==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
RtlZeroMemory( PausePos, 3 );
|
|
|
|
CdDump(( 3,
|
|
"HitachiDeviceControl => IOCTL_CDROM_PAUSE_AUDIO recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PAUSE_AUDIO.OperationCode = HITACHI_PAUSE_AUDIO_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
PausePos,
|
|
3
|
|
);
|
|
|
|
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,
|
|
"HitachiDeviceControl => IOCTL_CDROM_RESUME_AUDIO recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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)
|
|
);
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
CdDump(( 5,
|
|
"HitachiDeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"HitachiDeviceControl => READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"HitachiDeviceControl => READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump(( 1,
|
|
"HitachiDeviceControl => READ_Q_CHANNEL failed (%lx)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
{
|
|
|
|
PUCHAR EjectStatus = ExAllocatePool( NonPagedPoolCacheAligned, 1 );
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (EjectStatus==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
CdDump(( 3,
|
|
"HitachiDeviceControl => IOCTL_CDROM_EJECT_MEDIA recv'd.\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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
EjectStatus,
|
|
1
|
|
);
|
|
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" ));
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((10,"HitachiDeviceControl => 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_PASS_THROUGH 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;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(ULONG)(FIELD_OFFSET(CDROM_TOC, TrackData[1]))) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
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;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
lastSession,
|
|
sizeof(READ_CAPACITY_DATA)
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"535DeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
|
|
ExFreePool( lastSession );
|
|
Irp->IoStatus.Information = 0;
|
|
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-2) >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)((bytesTransfered-2) & 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,
|
|
"535DeviceControl => Tracks %d - %d, (%x 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,
|
|
"535DeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"535DeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
if (status != STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "535DeviceControl => SRB ERROR (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into SCSI-II format
|
|
// (track numbers, except 0xAA, must be converted from BCD)
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength >
|
|
sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->Length[0] = Toc[0];
|
|
cdaudioDataOut->Length[1] = Toc[1];
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[2]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[3]);
|
|
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; 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];
|
|
CdDump(( 4,
|
|
"CdAudio535DeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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];
|
|
CdDump(( 4,
|
|
"CdAudio535DeviceControl: 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]
|
|
));
|
|
i++;
|
|
}
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
);
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
// we have transferred zero bytes
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
|
|
CdDump(( 5,
|
|
"535DeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"535DeviceControl => READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"535DeviceControl => 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;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
);
|
|
|
|
//
|
|
// 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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump(( 1,
|
|
"535DeviceControl => READ_Q_CHANNEL failed (%lx)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
{
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Play Audio MSF
|
|
//
|
|
|
|
CdDump((2,"535DeviceControl: Play audio MSF\n"));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
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 = SendSrbSynchronous(deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0);
|
|
|
|
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,
|
|
"535DeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\n"
|
|
));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 3,
|
|
"535DeviceControl => SEEK failed (%lx)\n",
|
|
status ));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"535DeviceControl => IOCTL_CDROM_EJECT_MEDIA recv'd.\n"
|
|
));
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS535_EJECT_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
CdDump(( 3, "535DeviceControl => Not Supported yet.\n" ));
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((10,"535DeviceControl => 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_PASS_THROUGH 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,
|
|
"435DeviceControl => IOCTL_CDROM_READ_TOC recv'd.\n"
|
|
));
|
|
|
|
//
|
|
// Must have allocated at least enough buffer space
|
|
// to store how many tracks are on the disc
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => READ_TOC error (%lx)\n",
|
|
status ));
|
|
|
|
if (status != STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "435DeviceControl => SRB ERROR (%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into SCSI-II format
|
|
// (track numbers, except 0xAA, must be converted from BCD)
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength >
|
|
sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) :
|
|
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
cdaudioDataOut->Length[0] = Toc[0];
|
|
cdaudioDataOut->Length[1] = Toc[1];
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[2]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[3]);
|
|
|
|
//
|
|
// Return only N number of tracks, where N is the number of
|
|
// full tracks of info we can stuff into the user buffer
|
|
// if tracks from 1 to 2, that means there are two tracks,
|
|
// so let i go from 0 to 1 (two tracks of info)
|
|
//
|
|
{
|
|
//
|
|
// tracksToReturn == Number of real track info to return
|
|
// tracksInBuffer == How many fit into the user-supplied buffer
|
|
// tracksOnCd == Number of tracks on the CD (not including lead-out)
|
|
//
|
|
|
|
ULONG tracksToReturn;
|
|
ULONG tracksOnCd;
|
|
ULONG tracksInBuffer;
|
|
tracksOnCd = (cdaudioDataOut->LastTrack - cdaudioDataOut->FirstTrack) + 1;
|
|
tracksInBuffer = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
|
|
((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[0]));
|
|
tracksInBuffer /= sizeof(TRACK_DATA);
|
|
|
|
// take the lesser of the two
|
|
tracksToReturn = (tracksInBuffer < tracksOnCd) ?
|
|
tracksInBuffer :
|
|
tracksOnCd;
|
|
|
|
for( i=0; i < tracksToReturn; 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];
|
|
|
|
CdDump(( 4,
|
|
"CdAudio435DeviceControl: 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
|
|
// Only if all tracks have been copied...
|
|
//
|
|
|
|
if ( tracksInBuffer > tracksOnCd ) {
|
|
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];
|
|
CdDump(( 4,
|
|
"CdAudio435DeviceControl: 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]
|
|
));
|
|
i++;
|
|
}
|
|
|
|
Irp->IoStatus.Information = ((ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[i]));
|
|
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_STOP_AUDIO_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"435DeviceControl => STOP failed (%lx)\n",
|
|
status ));
|
|
|
|
}
|
|
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_CDROM_STOP_AUDIO
|
|
) {
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => IOCTL_CDROM_STOP_AUDIO recv'd.\n"
|
|
));
|
|
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => IOCTL_CDROM_PLAY_AUDIO_MSF recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"435DeviceControl => PLAY ==> BcdLastEnd set to (%x %x %x)\n",
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => PLAY failed (%lx)\n",
|
|
status ));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => IOCTL_CDROM_SEEK_AUDIO_MSF recv'd.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_SEEK_AUDIO_MSF)
|
|
) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
"435DeviceControl => 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,
|
|
"435DeviceControl => SEEK failed (%lx)\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)
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => IOCTL_CDROM_PAUSE_AUDIO recv'd.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => Pause, Read Q Channel failed (%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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => PAUSE, StopAudio failed! (%lx)\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,
|
|
"435DeviceControl => 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,
|
|
"435DeviceControl => IOCTL_CDROM_RESUME_AUDIO recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// 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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
} else {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => RESUME (%x %x %x) - (%x %x %x) failed (%lx)\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)
|
|
);
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = 0;
|
|
if (SubQPtr) ExFreePool(SubQPtr);
|
|
break;
|
|
}
|
|
|
|
|
|
CdDump(( 5,
|
|
"435DeviceControl => IOCTL_CDROM_READ_Q_CHANNEL recv'd.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"435DeviceControl => 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;
|
|
Irp->IoStatus.Information = 0;
|
|
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 = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
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 = sizeof(SUB_Q_CURRENT_POSITION);
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
CdDump(( 1,
|
|
"435DeviceControl => READ_Q_CHANNEL failed (%lx)\n",
|
|
status
|
|
));
|
|
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"435DeviceControl => IOCTL_CDROM_EJECT_MEDIA recv'd.\n"
|
|
));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_EJECT_CODE;
|
|
status = SendSrbSynchronous( deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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, "435DeviceControl => Not Supported yet.\n" ));
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
CdDump(( 3, "435DeviceControl => IOCTL_CDROM_CHECK_VERIFY recv'd.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject) == TRUE) {
|
|
deviceExtension->PlayActive = TRUE;
|
|
status = STATUS_SUCCESS; // media must be in place if audio
|
|
Irp->IoStatus.Information = 0; // is playing
|
|
goto SetStatusAndReturn;
|
|
} else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
CdDump((10,"435DeviceControl => 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
|
|
CdAudioAtapiDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
SCSI_PASS_THROUGH srb;
|
|
PHITACHICDB cdb = (PHITACHICDB)srb.Cdb;
|
|
|
|
CdDump ((3,"AtapiDeviceControl => 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
|
|
) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Zero and fill in new Srb
|
|
//
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_PASS_THROUGH));
|
|
|
|
//
|
|
// Issue the Atapi STOP_PLAY command.
|
|
//
|
|
|
|
cdb->STOP_PLAY.OperationCode = 0x4E;
|
|
|
|
srb.CdbLength = 12;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
|
|
status = SendSrbSynchronous(deviceExtension,
|
|
&srb,
|
|
NULL,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"AtapiDeviceControl => STOP_AUDIO error (%lx)\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;
|
|
}
|
|
|
|
CdDump((2, "HP TOC data for last session\n"));
|
|
for (cp = (PUCHAR) Toc, index = 0; index < 12; index++, cp++) {
|
|
CdDump((2, "%2x ", *cp));
|
|
}
|
|
CdDump((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.
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioForwardIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the Irp to the next driver in line
|
|
when the Irp needs to be processed by the lower drivers
|
|
prior to being processed by this one.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
deviceExtension = (PCD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// copy the irpstack for the next device
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
//
|
|
// set a completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp, CdAudioSignalCompletion,
|
|
&event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// call the next lower device
|
|
//
|
|
|
|
status = IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
//
|
|
// wait for the actual completion
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end DiskPerfForwardIrpSynchronous()
|
|
|
|
|
|
VOID CdAudioUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the control panel "Unloads"
|
|
the CDROM device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
CdDump((1,
|
|
"Unload => Unloading for DeviceObject %p\n",
|
|
DriverObject->DeviceObject
|
|
));
|
|
ASSERT(!DriverObject->DeviceObject);
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension;
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
deviceExtension = (PCD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
return PoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
#include "stdarg.h"
|
|
#define DBGHDR "[cdaudio] "
|
|
|
|
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];
|
|
DbgPrint(DBGHDR);
|
|
vsprintf(buffer, DebugMessage, ap);
|
|
DbgPrint(buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
#endif // DBG
|