windows-nt/Source/XPSP1/NT/drivers/storage/redbook/ioctl.c
2020-09-26 16:20:57 +08:00

1603 lines
44 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1998 - 1999
Module Name:
RedBook.c
Abstract:
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "redbook.h"
#include "ntddredb.h"
#include "proto.h"
#include <scsi.h> // for SetKnownGoodDrive()
#include <stdio.h> // vsprintf()
#include "ioctl.tmh"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RedBookCheckForDiscChangeAndFreeResources )
#pragma alloc_text(PAGE, RedBookCompleteIoctl )
#pragma alloc_text(PAGE, RedBookDCCheckVerify )
#pragma alloc_text(PAGE, RedBookDCDefault )
#pragma alloc_text(PAGE, RedBookDCGetVolume )
#pragma alloc_text(PAGE, RedBookDCPause )
#pragma alloc_text(PAGE, RedBookDCPlay )
#pragma alloc_text(PAGE, RedBookDCReadQ )
#pragma alloc_text(PAGE, RedBookDCResume )
#pragma alloc_text(PAGE, RedBookDCSeek )
#pragma alloc_text(PAGE, RedBookDCSetVolume )
#pragma alloc_text(PAGE, RedBookDCStop )
#pragma alloc_text(PAGE, RedBookThreadIoctlCompletionHandler )
#pragma alloc_text(PAGE, RedBookThreadIoctlHandler )
#pragma alloc_text(PAGE, WhichTrackContainsThisLBA )
#endif // ALLOC_PRAGMA
////////////////////////////////////////////////////////////////////////////////
NTSTATUS
RedBookDeviceControl(
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
--*/
{
PREDBOOK_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation( Irp );
ULONG cdromState;
NTSTATUS status;
BOOLEAN putOnQueue = FALSE;
BOOLEAN completeRequest = FALSE;
//
// ioctls not guaranteed at passive, making this whole
// section non-paged
//
//
// Prevent a remove from occuring while IO pending
//
status = IoAcquireRemoveLock( &deviceExtension->RemoveLock, Irp );
if ( !NT_SUCCESS(status) ) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DeviceControl !! Unable to acquire remove lock %lx\n",
status));
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_CD_ROM_INCREMENT );
return status;
}
#if DBG
//
// save some info for the last N device ioctls that came through
//
{
ULONG index;
ULONG sizeToCopy;
ULONG stacksToCopy;
PSAVED_IO savedIo;
index = InterlockedIncrement(&deviceExtension->SavedIoCurrentIndex);
index %= SAVED_IO_MAX;
savedIo = &(deviceExtension->SavedIo[index]);
//
// copy as much of the irp as we can....
//
savedIo->OriginalIrp = Irp;
if (Irp->StackCount > 7) {
sizeToCopy = IoSizeOfIrp(8);
RtlFillMemory(savedIo, sizeToCopy, 0xff);
sizeToCopy -= sizeof(IO_STACK_LOCATION);
RtlCopyMemory(savedIo, Irp, sizeToCopy);
} else {
sizeToCopy = IoSizeOfIrp(Irp->StackCount);
RtlZeroMemory(savedIo, sizeof(SAVED_IO));
RtlCopyMemory(savedIo, Irp, sizeToCopy);
}
} // end of saved io
#endif // DBG
//
// if handled, just verify the paramters in this routine.
//
status = STATUS_UNSUCCESSFUL;
cdromState = GetCdromState(deviceExtension);
switch ( currentIrpStack->Parameters.DeviceIoControl.IoControlCode ) {
case IOCTL_CDROM_PAUSE_AUDIO: {
if (TEST_FLAG(cdromState, CD_STOPPED)) {
Irp->IoStatus.Information = 0;
status = STATUS_INVALID_DEVICE_REQUEST;
completeRequest = TRUE;
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_STOP_AUDIO: {
if (TEST_FLAG(cdromState, CD_STOPPED)) {
Irp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
completeRequest = TRUE;
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_RESUME_AUDIO: {
if (TEST_FLAG(cdromState, CD_STOPPED)) {
Irp->IoStatus.Information = 0;
status = STATUS_INVALID_DEVICE_REQUEST;
completeRequest = TRUE;
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CDROM_PLAY_AUDIO_MSF)) {
Irp->IoStatus.Information = sizeof(CDROM_PLAY_AUDIO_MSF);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CDROM_SEEK_AUDIO_MSF)) {
Irp->IoStatus.Information = sizeof(CDROM_SEEK_AUDIO_MSF);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
// REQUIRED to reduce latency for some drives.
// drives may still fail the request
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_READ_Q_CHANNEL: {
PCDROM_SUB_Q_DATA_FORMAT inputBuffer;
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SUB_Q_CHANNEL_DATA)) {
Irp->IoStatus.Information = sizeof(SUB_Q_CHANNEL_DATA);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else if (inputBuffer->Format != IOCTL_CDROM_CURRENT_POSITION &&
inputBuffer->Format != IOCTL_CDROM_MEDIA_CATALOG &&
inputBuffer->Format != IOCTL_CDROM_TRACK_ISRC ) {
Irp->IoStatus.Information = 0;
status = STATUS_INVALID_PARAMETER;
completeRequest = TRUE;
} else if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_SET_VOLUME: {
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(VOLUME_CONTROL)) {
Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
// BUGBUG -- this should set our internal volume
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_CDROM_GET_VOLUME: {
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(VOLUME_CONTROL)) {
Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
// BUGBUG -- this should return our internal volume
} else {
putOnQueue = TRUE;
}
break;
}
case IOCTL_STORAGE_CHECK_VERIFY2:
case IOCTL_STORAGE_CHECK_VERIFY:
case IOCTL_CDROM_CHECK_VERIFY:
case IOCTL_DISK_CHECK_VERIFY: {
if ((currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength) &&
(currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG))) {
Irp->IoStatus.Information = sizeof(ULONG);
status = STATUS_BUFFER_TOO_SMALL;
completeRequest = TRUE;
} else if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
} else {
putOnQueue = TRUE;
}
break;
}
default: {
if (TEST_FLAG(cdromState, CD_STOPPED)) {
// default -- passthrough
} else {
putOnQueue = TRUE;
}
break;
}
}
if (putOnQueue) {
PREDBOOK_THREAD_IOCTL_DATA ioctlData;
ASSERT(completeRequest == FALSE);
//
// need to allocate some info for each ioctl we handle
//
ioctlData =
(PREDBOOK_THREAD_IOCTL_DATA)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(REDBOOK_THREAD_IOCTL_DATA),
TAG_T_IOCTL);
if (ioctlData == NULL) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_NO_MEMORY;
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
return STATUS_NO_MEMORY;
}
RtlZeroMemory(ioctlData, sizeof(REDBOOK_THREAD_IOCTL_DATA));
ioctlData->Irp = Irp;
ioctlData->Irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(ioctlData->Irp);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DeviceControl => Queue Ioctl Irp %p (%p)\n",
ioctlData->Irp, ioctlData));
//
// queue them, allow thread to handle request
//
ExInterlockedInsertTailList(&deviceExtension->Thread.IoctlList,
&ioctlData->ListEntry,
&deviceExtension->Thread.IoctlLock);
KeSetEvent(deviceExtension->Thread.Events[EVENT_IOCTL],
IO_NO_INCREMENT, FALSE);
status = STATUS_PENDING;
} else if (completeRequest) {
ASSERT(putOnQueue == FALSE);
//
// some error, ie. invalid buffer length
//
if (!NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DeviceControl => Completing Irp %p with error %x\n",
Irp, status));
} else {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DeviceControl => Completing Irp %p early?\n",
Irp));
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
} else {
//
// pass it through
//
status = RedBookSendToNextDriver(DeviceObject, Irp);
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
}
return status;
}
////////////////////////////////////////////////////////////////////////////////
VOID
RedBookThreadIoctlCompletionHandler(
PREDBOOK_DEVICE_EXTENSION DeviceExtension
)
{
PIO_STACK_LOCATION irpStack;
PREDBOOK_THREAD_IOCTL_DATA ioctlData;
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
ioctlData = CONTAINING_RECORD(DeviceExtension->Thread.IoctlCurrent,
REDBOOK_THREAD_IOCTL_DATA,
ListEntry);
state = GetCdromState(DeviceExtension);
irpStack = IoGetCurrentIrpStackLocation(ioctlData->Irp);
//
// final state should be set by the digital handler
//
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_CDROM_PAUSE_AUDIO: {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"IoctlComp => Finishing pause %p\n", ioctlData->Irp));
ASSERT(state == CD_PAUSED);
ioctlData->Irp->IoStatus.Information = 0;
ioctlData->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, ioctlData, FALSE);
break;
}
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"IoctlComp => Finishing seek %p\n", ioctlData->Irp));
ASSERT(state == CD_PLAYING);
DeviceExtension->Buffer.FirstPause = 1;
KeSetEvent(DeviceExtension->Thread.Events[EVENT_DIGITAL],
IO_CD_ROM_INCREMENT, FALSE);
ioctlData->Irp->IoStatus.Information = 0;
ioctlData->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, ioctlData, FALSE);
break;
}
case IOCTL_CDROM_STOP_AUDIO: {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"IoctlComp => Finishing stop %p\n", ioctlData->Irp));
ASSERT(TEST_FLAG(state, CD_STOPPED));
ioctlData->Irp->IoStatus.Information = 0;
ioctlData->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, ioctlData, FALSE);
break;
}
default: {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"IoctlComp => Unhandled Irp %p\n", ioctlData->Irp));
ASSERT(FALSE);
ioctlData->Irp->IoStatus.Information = 0;
ioctlData->Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
RedBookCompleteIoctl(DeviceExtension, ioctlData, FALSE);
break;
}
}
return;
}
NTSTATUS
RedBookCompleteIoctl(
IN PREDBOOK_DEVICE_EXTENSION DeviceExtension,
IN PREDBOOK_THREAD_IOCTL_DATA Context,
IN BOOLEAN SendToLowerDriver
)
{
PIRP irp = Context->Irp;
//
// only to be called from the thread
//
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
//
// should be properly setup for completion
//
if (DeviceExtension->Thread.IoctlCurrent == &Context->ListEntry) {
//
// an ioctl that required post-processing is finished.
// allow the next one to occur
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"CompleteIoctl => state-changing Irp %p completed\n",
irp));
DeviceExtension->Thread.IoctlCurrent = NULL;
}
ExFreePool(Context);
Context = NULL;
if (SendToLowerDriver) {
NTSTATUS status;
status = RedBookSendToNextDriver(DeviceExtension->SelfDeviceObject, irp);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, irp);
return status;
} else {
IoCompleteRequest(irp, IO_CD_ROM_INCREMENT);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, irp);
return STATUS_SUCCESS;
}
}
VOID
RedBookThreadIoctlHandler(
IN PREDBOOK_DEVICE_EXTENSION DeviceExtension,
IN PLIST_ENTRY ListEntry
)
{
PREDBOOK_THREAD_IOCTL_DATA data;
PIO_STACK_LOCATION currentIrpStack;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
//
// should never happen if a state-changing ioctl is in progress
//
ASSERT(DeviceExtension->Thread.IoctlCurrent == NULL);
//
// don't use stale info
//
RedBookCheckForDiscChangeAndFreeResources(DeviceExtension);
//
// get the ioctl that set this event and
// start working on state changes neccessary
//
data = CONTAINING_RECORD(ListEntry, REDBOOK_THREAD_IOCTL_DATA, ListEntry);
currentIrpStack = IoGetCurrentIrpStackLocation(data->Irp);
//
// now guaranteed it's ok to run this ioctl
// it's the responsibility of these routines to call RedBookCompleteIoctl()
// *** OR *** to set DeviceExtension->Thread.IoctlCurrent to
// Context->ListEntry if it requires post-processing, as this
// is the mechanism used to determine the ioctl is still inprogress
//
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
RedBookDCPlay(DeviceExtension, data);
break;
}
case IOCTL_CDROM_PAUSE_AUDIO: {
RedBookDCPause(DeviceExtension, data);
break;
}
case IOCTL_CDROM_RESUME_AUDIO: {
RedBookDCResume(DeviceExtension, data);
break;
}
case IOCTL_CDROM_STOP_AUDIO: {
RedBookDCStop(DeviceExtension, data);
break;
}
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
RedBookDCSeek(DeviceExtension, data);
break;
}
case IOCTL_CDROM_READ_Q_CHANNEL: {
RedBookDCReadQ(DeviceExtension, data);
break;
}
case IOCTL_CDROM_SET_VOLUME: {
RedBookDCSetVolume(DeviceExtension, data);
break;
}
case IOCTL_CDROM_GET_VOLUME: {
RedBookDCGetVolume(DeviceExtension, data);
break;
}
case IOCTL_CDROM_CHECK_VERIFY:
case IOCTL_DISK_CHECK_VERIFY:
case IOCTL_STORAGE_CHECK_VERIFY:
case IOCTL_STORAGE_CHECK_VERIFY2: {
RedBookDCCheckVerify(DeviceExtension, data);
break;
}
default: {
data->Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
data->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, data, TRUE);
break;
}
}
return;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
VOID
RedBookDCCheckVerify(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PIO_STACK_LOCATION currentIrpStack;
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
state = GetCdromState(DeviceExtension);
currentIrpStack = IoGetCurrentIrpStackLocation(Context->Irp);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCCheckVerify => not playing\n"));
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
return;
}
//
// data buffer is optional for this ioctl
//
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
*((PULONG)Context->Irp->AssociatedIrp.SystemBuffer) =
DeviceExtension->CDRom.CheckVerify;
Context->Irp->IoStatus.Information = sizeof(ULONG);
} else {
Context->Irp->IoStatus.Information = 0;
}
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCDefault(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
state = GetCdromState(DeviceExtension);
//
// IOCTLs are not all guaranteed to be called at passive irql,
// so this can never be paged code.
// there is a window of opportunity to send an ioctl while playing
// audio digitally, but it can be ignored. this allows much more
// pagable code.
//
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
} else {
//
// Complete the Irp
//
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
}
return;
}
VOID
RedBookDCGetVolume(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PIO_STACK_LOCATION currentIrpStack;
NTSTATUS status;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCGetVolume => Entering %p\n", Context->Irp));
//
// guaranteed the volume info will not change
//
RtlCopyMemory(Context->Irp->AssociatedIrp.SystemBuffer, // to
&DeviceExtension->CDRom.Volume, // from
sizeof(VOLUME_CONTROL));
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCGetVolume => volume was:"
" (hex) %2x %2x %2x %2x\n",
DeviceExtension->CDRom.Volume.PortVolume[0],
DeviceExtension->CDRom.Volume.PortVolume[1],
DeviceExtension->CDRom.Volume.PortVolume[2],
DeviceExtension->CDRom.Volume.PortVolume[3]));
//
// Complete the Irp (IoStatus.Information set above)
//
Context->Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCPause(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
state = GetCdromState(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPause => Entering %p\n", Context->Irp));
if (!TEST_FLAG(state, CD_PLAYING)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPause => Not playing\n"));
Context->Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
return;
}
if (TEST_FLAG(state, CD_PAUSED)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPause => Already paused %p\n", Context->Irp));
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
} else {
//
// since setting to a temp state, it is not appropriate to
// complete the irp until the operation itself completes.
//
ASSERT(!TEST_FLAG(state, CD_PAUSING));
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPause => Starting pause %p\n", Context->Irp));
DeviceExtension->Thread.IoctlCurrent = &Context->ListEntry;
state = SetCdromState(DeviceExtension, state, state | CD_PAUSING);
return;
}
}
VOID
RedBookDCPlay(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PCDROM_PLAY_AUDIO_MSF inputBuffer;
PIO_STACK_LOCATION thisIrpStack;
PIO_STACK_LOCATION nextIrpStack;
PREVENT_MEDIA_REMOVAL mediaRemoval;
ULONG sector;
ULONG i;
ULONG state;
NTSTATUS status;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
inputBuffer = Context->Irp->AssociatedIrp.SystemBuffer;
thisIrpStack = IoGetCurrentIrpStackLocation(Context->Irp);
nextIrpStack = IoGetNextIrpStackLocation(Context->Irp);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPlay => Entering %p\n", Context->Irp));
status = RedBookCacheToc(DeviceExtension);
if (!NT_SUCCESS(status)) {
Context->Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
sector = MSF_TO_LBA(inputBuffer->EndingM,
inputBuffer->EndingS,
inputBuffer->EndingF);
DeviceExtension->CDRom.EndPlay = sector;
sector = MSF_TO_LBA(inputBuffer->StartingM,
inputBuffer->StartingS,
inputBuffer->StartingF);
DeviceExtension->CDRom.NextToRead = sector;
DeviceExtension->CDRom.NextToStream = sector;
DeviceExtension->CDRom.FinishedStreaming = sector;
//
// Make sure the ending sector is within disc
// bounds or return STATUS_INVALID_DEVICE_REQUEST?
// this will prevent success on play, followed by an
// immediate stop.
//
if (0) {
PCDROM_TOC toc = DeviceExtension->CDRom.Toc;
LONG track;
LONG endTrack;
//
// ensure end has an lba greater than start
//
if (DeviceExtension->CDRom.EndPlay <=
DeviceExtension->CDRom.NextToRead) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"Play => End sector (%x) must be more than start "
"sector (%x)\n",
DeviceExtension->CDRom.EndPlay,
DeviceExtension->CDRom.NextToRead
));
Context->Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// what track(s) are we playing?
//
track = WhichTrackContainsThisLBA(toc, DeviceExtension->CDRom.NextToRead);
endTrack = WhichTrackContainsThisLBA(toc, DeviceExtension->CDRom.EndPlay);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"Play => Playing sector %x to %x (track %x to %x)\n",
DeviceExtension->CDRom.NextToRead,
DeviceExtension->CDRom.EndPlay,
track,
endTrack));
//
// make sure the tracks are actually valid
//
if (track < 0 ||
endTrack < 0 ||
endTrack <= (toc->LastTrack - toc->FirstTrack)
) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"Play => Track %x is invalid\n", track));
Context->Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
for (;track <= endTrack;track++) {
if (toc->TrackData[track].Adr & AUDIO_DATA_TRACK) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"Play => Track %x is not audio\n", track));
Context->Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
}
}
//
// if not paused, then state must equal stopped, which means we need
// to allocate the resources.
//
state = GetCdromState(DeviceExtension);
if (TEST_FLAG(state, CD_PAUSED)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPlay => Resuming playback?\n"));
} else {
//
// this function will allocate them iff they are not
// already allocated.
//
status = RedBookAllocatePlayResources(DeviceExtension);
if (!NT_SUCCESS(status)) {
Context->Irp->IoStatus.Status = STATUS_NO_MEMORY;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
}
//
// Set the new device state (thread will begin playing)
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPlay => Setting state to CD_PLAYING\n"));
state = SetCdromState(DeviceExtension, state, CD_PLAYING);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCPlay => Exiting successfully\n"));
//
// finish the request if it's a user
// request for a new play operation
//
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
Context->Irp->IoStatus.Information = 0;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCReadQ(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PCDROM_SUB_Q_DATA_FORMAT inputBuffer;
PIO_STACK_LOCATION currentIrpStack;
UCHAR formatCode;
ULONG state;
NTSTATUS status;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
inputBuffer = Context->Irp->AssociatedIrp.SystemBuffer;
currentIrpStack = IoGetCurrentIrpStackLocation(Context->Irp);
state = GetCdromState(DeviceExtension);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
//
// no need to handle this irp
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCReadQ => Not playing\n"));
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
return;
}
if (inputBuffer->Format != IOCTL_CDROM_CURRENT_POSITION) {
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCReadQ => Bad Format %x\n", inputBuffer->Format));
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// we are in the midst of playback or pause. fake the information
// a real cdrom would have returned if it was playing audio at the
// same location that we are currently at.
//
{
PSUB_Q_CURRENT_POSITION outputBuffer;
PCDROM_TOC toc;
ULONG lbaTrack;
ULONG lbaRelative;
ULONG instantLba;
UCHAR timeAbsolute[3];
UCHAR timeRelative[3];
LONG trackNumber;
outputBuffer = Context->Irp->AssociatedIrp.SystemBuffer;
RtlZeroMemory(outputBuffer,
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength);
//
// Still playing audio
//
outputBuffer->Header.Reserved = 0;
if (TEST_FLAG(state, CD_PAUSED)) {
outputBuffer->Header.AudioStatus = AUDIO_STATUS_PAUSED;
} else if (TEST_FLAG(state, CD_PLAYING)) {
outputBuffer->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
} else {
ASSERT(!"State was invalid?");
outputBuffer->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
}
outputBuffer->Header.DataLength[0] =
(sizeof(SUB_Q_CURRENT_POSITION) - sizeof(SUB_Q_HEADER)) >> 8;
outputBuffer->Header.DataLength[1] =
(sizeof(SUB_Q_CURRENT_POSITION) - sizeof(SUB_Q_HEADER)) & 0xFF;
//
// we are in the thread, which alloc's/dealloc's the toc
//
toc = DeviceExtension->CDRom.Toc;
ASSERT(toc);
//
// we return the last played sector as a result per the spec
//
instantLba = DeviceExtension->CDRom.FinishedStreaming;
trackNumber = WhichTrackContainsThisLBA(toc, instantLba);
ASSERT(trackNumber >= 0);
outputBuffer->FormatCode = IOCTL_CDROM_CURRENT_POSITION;
outputBuffer->Control = toc->TrackData[trackNumber].Control;
outputBuffer->ADR = toc->TrackData[trackNumber].Adr;
outputBuffer->TrackNumber = toc->TrackData[trackNumber].TrackNumber;
//
// Get the track's LBA
//
lbaTrack = MSF_TO_LBA(toc->TrackData[trackNumber].Address[1],
toc->TrackData[trackNumber].Address[2],
toc->TrackData[trackNumber].Address[3]);
//
// Get the current play LBA
//
lbaRelative = instantLba;
//
// Subtract the track's LBA to get the relative LBA
//
lbaRelative -= lbaTrack;
//
// Finally convert it back to MSF
//
LBA_TO_MSF(instantLba,
timeAbsolute[0],
timeAbsolute[1],
timeAbsolute[2]);
LBA_TO_RELATIVE_MSF(lbaRelative,
timeRelative[0],
timeRelative[1],
timeRelative[2]);
outputBuffer->IndexNumber = (UCHAR)trackNumber;
outputBuffer->AbsoluteAddress[0] = 0;
outputBuffer->AbsoluteAddress[1] = timeAbsolute[0];
outputBuffer->AbsoluteAddress[2] = timeAbsolute[1];
outputBuffer->AbsoluteAddress[3] = timeAbsolute[2];
outputBuffer->TrackRelativeAddress[0] = 0;
outputBuffer->TrackRelativeAddress[1] = timeRelative[0];
outputBuffer->TrackRelativeAddress[2] = timeRelative[1];
outputBuffer->TrackRelativeAddress[3] = timeRelative[2];
//
// The one line debug info...
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctlV, "[redbook] "
"ReadQ => "
"Trk [%#02x] Indx [%#02x] "
"Abs [%#02d:%#02d.%#02d] Rel [%#02d:%#02d.%#02d]\n",
outputBuffer->TrackNumber,
trackNumber,
timeAbsolute[0], timeAbsolute[1], timeAbsolute[2],
timeRelative[0], timeRelative[1], timeRelative[2]));
}
//
// Complete the Irp
//
Context->Irp->IoStatus.Information = sizeof(SUB_Q_CURRENT_POSITION);
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCResume(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
state = GetCdromState(DeviceExtension);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCResume => Not Playing\n"));
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
return;
}
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCResume => Entering\n"));
if (TEST_FLAG(state, CD_PAUSED)) {
//
// we need to start the resume operation
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCResume => Resuming playback\n"));
state = SetCdromState(DeviceExtension, state, CD_PLAYING);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCResume => Resume succeeded\n"));
} else {
//
// if not paused, return success
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCResume => Not paused -- succeeded\n"));
}
//
// always complete the Irp
//
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCSeek(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
same as a IOCTL_CDROM_STOP
Arguments:
Return Value:
--*/
{
NTSTATUS status;
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSeek => Entering\n"));
state = GetCdromState(DeviceExtension);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSeek => Not Playing\n"));
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// stop the stream if currently playing
//
if (TEST_FLAG(state, CD_PAUSED)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSeek => Paused, setting to stopped\n"));
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// since setting to a temp state, it is not appropriate to
// complete the irp until the operation itself completes.
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSeek => stopping the stream\n"));
DeviceExtension->Thread.IoctlCurrent = &Context->ListEntry;
state = SetCdromState(DeviceExtension, state, state | CD_STOPPING);
return;
}
VOID
RedBookDCSetVolume(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PIO_STACK_LOCATION currentIrpStack;
ULONG state;
NTSTATUS status;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSetVolume => Entering\n"));
//
// guaranteed the volume info will not change right now
//
RtlCopyMemory(&DeviceExtension->CDRom.Volume, // to
Context->Irp->AssociatedIrp.SystemBuffer, // from
sizeof(VOLUME_CONTROL));
state = GetCdromState(DeviceExtension);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCSetVolume => Not Playing\n"));
RedBookCompleteIoctl(DeviceExtension, Context, TRUE);
return;
}
//
// not set above since don't have volume control
//
RedBookKsSetVolume(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctlV, "[redbook] "
"DCSetVolume => volume set to:"
" (hex) %2x %2x %2x %2x\n",
DeviceExtension->CDRom.Volume.PortVolume[0],
DeviceExtension->CDRom.Volume.PortVolume[1],
DeviceExtension->CDRom.Volume.PortVolume[2],
DeviceExtension->CDRom.Volume.PortVolume[3]));
//
// Complete the Irp (IoStatus.Information set above)
//
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
VOID
RedBookDCStop(
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
PREDBOOK_THREAD_IOCTL_DATA Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS status;
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCStop => Entering %p\n", Context->Irp));
state = GetCdromState(DeviceExtension);
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCStop => Stop when already stopped\n"));
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// Still playing audio. if paused, just call the stop finish routine
//
if (TEST_FLAG(state, CD_PAUSED)) {
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCStop => Stop when paused\n"));
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
Context->Irp->IoStatus.Information = 0;
Context->Irp->IoStatus.Status = STATUS_SUCCESS;
RedBookCompleteIoctl(DeviceExtension, Context, FALSE);
return;
}
//
// since setting to a temp state, it is not appropriate to
// complete the irp until the operation itself completes.
//
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugIoctl, "[redbook] "
"DCStop => stopping the stream\n"));
DeviceExtension->Thread.IoctlCurrent = &Context->ListEntry;
state = SetCdromState(DeviceExtension, state, state | CD_STOPPING);
return;
}
////////////////////////////////////////////////////////////////////////////////
VOID
RedBookCheckForDiscChangeAndFreeResources(
PREDBOOK_DEVICE_EXTENSION DeviceExtension
)
//
// if we've paused, and the disc has changed, don't
// want to be returning stale toc info when the player
// resumes playback.
//
{
PIRP irp;
PIO_STACK_LOCATION irpStack;
PULONG count;
ULONG state;
PAGED_CODE();
VerifyCalledByThread(DeviceExtension);
//
// only do this if we are in a PAUSED or STOPPED state
//
state = GetCdromState(DeviceExtension);
if ((!TEST_FLAG(state, CD_STOPPED)) &&
(!TEST_FLAG(state, CD_PAUSED))) {
return;
}
//
// resources might already be deallocated.
//
irp = DeviceExtension->Thread.CheckVerifyIrp;
if (irp == NULL) {
return;
}
irpStack = IoGetCurrentIrpStackLocation(irp);
#if DBG
{
//
// the irp must be setup when it's allocated. we rely on this.
//
ASSERT(irpStack->Parameters.DeviceIoControl.InputBufferLength == 0);
ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength ==
sizeof(ULONG));
ASSERT(irpStack->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_CDROM_CHECK_VERIFY);
ASSERT(irpStack->Parameters.DeviceIoControl.Type3InputBuffer == NULL);
ASSERT(irp->AssociatedIrp.SystemBuffer != NULL);
}
#endif
count = (PULONG)(irp->AssociatedIrp.SystemBuffer);
*count = 0;
RedBookForwardIrpSynchronous(DeviceExtension, irp);
if (!NT_SUCCESS(irp->IoStatus.Status) ||
((*count) != DeviceExtension->CDRom.CheckVerify)
) {
//
// if the count has changed set the state to STOPPED.
// (old state is either STOPPED or PAUSED, so either one can
// seemlessly transition to the STOPPED state without any
// trouble.)
//
// also free currently held play resources
//
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
RedBookDeallocatePlayResources(DeviceExtension);
}
return;
}
ULONG
WhichTrackContainsThisLBA(
PCDROM_TOC Toc,
ULONG Lba
)
//
// returns -1 if not found
//
{
LONG trackNumber;
UCHAR msf[3];
PAGED_CODE();
LBA_TO_MSF(Lba, msf[0], msf[1], msf[2]);
for (trackNumber = Toc->LastTrack - Toc->FirstTrack;
trackNumber >= 0;
trackNumber-- ) {
//
// we have found the track if
// Minutes is less or
// Minutes is equal and Seconds is less or
// Minutes and Seconds are equal Frame is less or
// Minutes, Seconds, and Frame are equal
//
// the compiler optimizes this nicely.
//
if (Toc->TrackData[trackNumber].Address[1] < msf[0] ) {
break;
} else
if (Toc->TrackData[trackNumber].Address[1] == msf[0] &&
Toc->TrackData[trackNumber].Address[2] < msf[1] ) {
break;
} else
if (Toc->TrackData[trackNumber].Address[1] == msf[0] &&
Toc->TrackData[trackNumber].Address[2] == msf[1] &&
Toc->TrackData[trackNumber].Address[3] < msf[2] ) {
break;
} else
if (Toc->TrackData[trackNumber].Address[1] == msf[0] &&
Toc->TrackData[trackNumber].Address[2] == msf[1] &&
Toc->TrackData[trackNumber].Address[3] == msf[2] ) {
break;
}
}
return trackNumber;
}