2167 lines
67 KiB
C
2167 lines
67 KiB
C
/*++
|
||
Copyright (C) Microsoft Corporation, 1998 - 1999
|
||
|
||
Module Name:
|
||
|
||
thread.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 "thread.tmh"
|
||
|
||
//
|
||
// this is how many seconds until resources are free'd from
|
||
// a play, and also how long (in seconds) before a frozen state
|
||
// is detected (and a fix attempted).
|
||
//
|
||
|
||
#define REDBOOK_THREAD_FIXUP_SECONDS 10
|
||
#define REDBOOK_THREAD_SYSAUDIO_CACHE_SECONDS 2
|
||
#define REDBOOK_PERFORM_STUTTER_CONTROL 0
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// allows me to send silence to ks as needed
|
||
//
|
||
|
||
ULONG RedBookForceSilence = FALSE;
|
||
|
||
#endif
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(PAGE, RedBookAllocatePlayResources )
|
||
#pragma alloc_text(PAGE, RedBookCacheToc )
|
||
#pragma alloc_text(PAGE, RedBookDeallocatePlayResources )
|
||
#pragma alloc_text(PAGERW, RedBookReadRaw )
|
||
#pragma alloc_text(PAGERW, RedBookStream )
|
||
#pragma alloc_text(PAGE, RedBookSystemThread )
|
||
#pragma alloc_text(PAGE, RedBookCheckForAudioDeviceRemoval )
|
||
#pragma alloc_text(PAGE, RedBookThreadDigitalHandler )
|
||
|
||
/*
|
||
but last two CANNOT be unlocked when playing,
|
||
so they are (temporarily) commented out.
|
||
eventually will only have them locked when playing
|
||
|
||
#pragma alloc_text(PAGERW, RedBookReadRawCompletion )
|
||
#pragma alloc_text(PAGERW, RedBookStreamCompletion )
|
||
|
||
*/
|
||
|
||
#endif ALLOC_PRAGMA
|
||
|
||
|
||
VOID
|
||
RedBookSystemThread(
|
||
PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This system thread will wait on events,
|
||
sending buffers to Kernel Streaming as they
|
||
become available.
|
||
|
||
Arguments:
|
||
|
||
Context - deviceExtension
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PREDBOOK_DEVICE_EXTENSION deviceExtension = Context;
|
||
LARGE_INTEGER timeout;
|
||
NTSTATUS waitStatus;
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
ULONG timeouts;
|
||
LARGE_INTEGER stopTime;
|
||
|
||
//
|
||
// some per-thread state
|
||
//
|
||
|
||
BOOLEAN killed = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
deviceExtension->Thread.SelfPointer = PsGetCurrentThread();
|
||
|
||
//
|
||
// perf fix -- run at low realtime priority
|
||
//
|
||
|
||
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
|
||
|
||
timeouts = 0;
|
||
stopTime.QuadPart = 0;
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Thread => UpdateMixerPin at %p\n",
|
||
&deviceExtension->Stream.UpdateMixerPin));
|
||
|
||
//
|
||
// Just loop waiting for events.
|
||
//
|
||
|
||
//waitForNextEvent:
|
||
while ( 1 ) {
|
||
|
||
//
|
||
// if are killed, just wait on singular event--EVENT_DIGITAL until
|
||
// can finish processing. there should not be anything left in the
|
||
// IOCTL_LIST (since the kill calls RemoveLockAndWait() first)
|
||
//
|
||
// this also implies that a kill will never occur while ioctls are
|
||
// still being processed. this does not guarantee the state will
|
||
// be STOPPED, just that no IO will be occurring.
|
||
//
|
||
|
||
|
||
//
|
||
// nanosecond is 10^-9, units is 100 nanoseconds
|
||
// so seconds is 10,000,000 units
|
||
// must wait in relative time, which requires negative numbers
|
||
//
|
||
|
||
timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000);
|
||
|
||
//
|
||
// note: we are using a timeout mechanism mostly to catch bugs
|
||
// where the state would lock up. we also "auto adjust"
|
||
// our internal state if things get too wierd, basically
|
||
// auto-fixing ourselves. note that this does cause the
|
||
// thread's stack to be swapped in, so this shouldn't
|
||
// be done when we're 100% stopped.
|
||
//
|
||
|
||
if (deviceExtension->Thread.IoctlCurrent == NULL) {
|
||
|
||
//
|
||
// wait on an ioctl, but not the ioctl completion event
|
||
//
|
||
|
||
ULONG state = GetCdromState(deviceExtension);
|
||
if ((state == CD_STOPPED) &&
|
||
(!RedBookArePlayResourcesAllocated(deviceExtension))
|
||
) {
|
||
|
||
//
|
||
// if we've got no current ioctl and we haven't allocated
|
||
// any resources, there're no need to timeout.
|
||
// this will prevent this stack from getting swapped in
|
||
// needlessly, reducing effective footprint a bit
|
||
//
|
||
|
||
stopTime.QuadPart = 0;
|
||
waitStatus = KeWaitForMultipleObjects(EVENT_MAXIMUM - 1,
|
||
(PVOID)(&deviceExtension->Thread.Events[0]),
|
||
WaitAny,
|
||
Executive,
|
||
UserMode,
|
||
FALSE, // Alertable
|
||
NULL,
|
||
deviceExtension->Thread.EventBlock
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// we've got no current ioctl, but we're also not stopped.
|
||
// it is also possible to be waiting to cleanup resources here.
|
||
// even if we're paused, we want to keep track of what's
|
||
// going on, since it's possible for the state to get
|
||
// messed up here.
|
||
//
|
||
|
||
waitStatus = KeWaitForMultipleObjects(EVENT_MAXIMUM - 1,
|
||
(PVOID)(&deviceExtension->Thread.Events[0]),
|
||
WaitAny,
|
||
Executive,
|
||
UserMode,
|
||
FALSE, // Alertable
|
||
&timeout,
|
||
deviceExtension->Thread.EventBlock
|
||
);
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// wait on the ioctl completion, but not the ioctl event
|
||
//
|
||
|
||
waitStatus = KeWaitForMultipleObjects(EVENT_MAXIMUM - 1,
|
||
(PVOID)(&deviceExtension->Thread.Events[1]),
|
||
WaitAny,
|
||
Executive,
|
||
UserMode,
|
||
FALSE, // Alertable
|
||
&timeout,
|
||
deviceExtension->Thread.EventBlock
|
||
);
|
||
if (waitStatus != STATUS_TIMEOUT) {
|
||
waitStatus ++; // to account for offset
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// need to check if we are stopped for too long -- if so, free
|
||
// the resources.
|
||
//
|
||
{
|
||
ULONG state = GetCdromState(deviceExtension);
|
||
|
||
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) {
|
||
|
||
LARGE_INTEGER now;
|
||
|
||
// not playing,
|
||
if (stopTime.QuadPart == 0) {
|
||
|
||
LONGLONG offset;
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"StopTime => Determining when to dealloc\n"));
|
||
|
||
// query the time
|
||
KeQueryTickCount( &stopTime );
|
||
|
||
// add appropriate offset
|
||
// nanosecond is 10^-9, units is 100 nanoseconds
|
||
// so seconds is 10,000,000 units
|
||
//
|
||
offset = REDBOOK_THREAD_SYSAUDIO_CACHE_SECONDS;
|
||
offset *= (LONGLONG)(10 * 1000 * (LONGLONG)1000);
|
||
|
||
// divide offset by time increment
|
||
offset /= (LONGLONG)KeQueryTimeIncrement();
|
||
|
||
// add those ticks to store when we should release
|
||
// our resources
|
||
stopTime.QuadPart += offset;
|
||
|
||
}
|
||
|
||
KeQueryTickCount(&now);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"StopTime => Is %I64x >= %I64x?\n",
|
||
now.QuadPart,
|
||
stopTime.QuadPart
|
||
));
|
||
|
||
if (now.QuadPart >= stopTime.QuadPart) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"StopTime => Deallocating resources\n"));
|
||
RedBookDeallocatePlayResources(deviceExtension);
|
||
}
|
||
|
||
} else {
|
||
|
||
stopTime.QuadPart = 0;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
RedBookCheckForAudioDeviceRemoval(deviceExtension);
|
||
|
||
//
|
||
// To enable a single thread for multiple cdroms, just set events
|
||
// at offset of (DEVICE_ID * EVENT_MAX) + EVENT_TO_SET
|
||
// set deviceExtension = waitStatus / EVENT_MAX
|
||
// switch ( waitStatus % EVENT_MAX )
|
||
//
|
||
if (waitStatus == EVENT_DIGITAL) {
|
||
timeouts = 0;
|
||
}
|
||
|
||
switch ( waitStatus ) {
|
||
|
||
case EVENT_IOCTL: {
|
||
|
||
PLIST_ENTRY listEntry;
|
||
while ((listEntry = ExInterlockedRemoveHeadList(
|
||
&deviceExtension->Thread.IoctlList,
|
||
&deviceExtension->Thread.IoctlLock)) != NULL) {
|
||
|
||
RedBookThreadIoctlHandler(deviceExtension, listEntry);
|
||
|
||
if (deviceExtension->Thread.IoctlCurrent) {
|
||
// special case
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
break;
|
||
}
|
||
case EVENT_COMPLETE: {
|
||
RedBookThreadIoctlCompletionHandler(deviceExtension);
|
||
break;
|
||
}
|
||
|
||
case EVENT_WMI: {
|
||
|
||
PLIST_ENTRY listEntry;
|
||
while ((listEntry = ExInterlockedRemoveHeadList(
|
||
&deviceExtension->Thread.WmiList,
|
||
&deviceExtension->Thread.WmiLock)) != NULL) {
|
||
|
||
RedBookThreadWmiHandler(deviceExtension, listEntry);
|
||
|
||
}
|
||
break;
|
||
}
|
||
|
||
case EVENT_DIGITAL: {
|
||
|
||
PLIST_ENTRY listEntry;
|
||
while ((listEntry = ExInterlockedRemoveHeadList(
|
||
&deviceExtension->Thread.DigitalList,
|
||
&deviceExtension->Thread.DigitalLock)) != NULL) {
|
||
|
||
RedBookThreadDigitalHandler(deviceExtension, listEntry);
|
||
|
||
}
|
||
break;
|
||
}
|
||
|
||
case EVENT_KILL_THREAD: {
|
||
|
||
ULONG state = GetCdromState(deviceExtension);
|
||
|
||
killed = TRUE;
|
||
|
||
//
|
||
// i don't think this can occur with outstanding io, since
|
||
// the remove lock is taken first. nonetheless, it's better
|
||
// to be safe than sorry.
|
||
//
|
||
|
||
if (!TEST_FLAG(state, CD_STOPPED)) {
|
||
state = SetCdromState(deviceExtension, state, CD_STOPPING);
|
||
break;
|
||
}
|
||
|
||
RedBookDeallocatePlayResources(deviceExtension);
|
||
|
||
//
|
||
// else we can safely terminate.
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"STExit => Thread was killed\n"));
|
||
ASSERT(deviceExtension->Thread.PendingRead == 0);
|
||
ASSERT(deviceExtension->Thread.PendingStream == 0);
|
||
PsTerminateSystemThread(STATUS_SUCCESS);
|
||
ASSERT(!"[redbook] Thread should never reach past self-terminate code");
|
||
break;
|
||
}
|
||
|
||
case STATUS_TIMEOUT: {
|
||
|
||
ULONG state = GetCdromState(deviceExtension);
|
||
|
||
timeouts++;
|
||
|
||
if (timeouts < REDBOOK_THREAD_FIXUP_SECONDS) {
|
||
break;
|
||
} else {
|
||
timeouts = 0;
|
||
}
|
||
|
||
//
|
||
// these tests all occur once every ten seconds.
|
||
// the most basic case is where we want to deallocate
|
||
// our cached TOC, but we also perform lots of
|
||
// sanity testing here -- ASSERTing on CHK builds and
|
||
// trying to fix ourselves up when possible.
|
||
//
|
||
|
||
if (!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED)) { // !handling ioctls
|
||
|
||
// not playing, so free resources
|
||
RedBookDeallocatePlayResources(deviceExtension);
|
||
|
||
} else if (TEST_FLAG(state, CD_STOPPING)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
||
"STTime !! %x seconds inactivity\n",
|
||
REDBOOK_THREAD_FIXUP_SECONDS));
|
||
|
||
if (IsListEmpty(&deviceExtension->Thread.DigitalList)) {
|
||
|
||
if ((deviceExtension->Thread.PendingRead == 0) &&
|
||
(deviceExtension->Thread.PendingStream == 0) &&
|
||
(deviceExtension->Thread.IoctlCurrent != NULL)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError,
|
||
"[redbook] "
|
||
"STTime !! No Reads, No Streams, In a temp "
|
||
"state (%x) and have STOP irp %p "
|
||
"pending?!\n",
|
||
state,
|
||
((PREDBOOK_THREAD_IOCTL_DATA)deviceExtension->Thread.IoctlCurrent)->Irp
|
||
));
|
||
ASSERT(!"STTime !! CD_STOPPING Fixup with no reads nor streams but STOP pending\n");
|
||
SetCdromState(deviceExtension, state, CD_STOPPED);
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_COMPLETE],
|
||
IO_NO_INCREMENT, FALSE);
|
||
|
||
} else {
|
||
|
||
ASSERT(!"STTime !! CD_STOPPING Fixup with empty list and no pending ioctl?\n");
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
ASSERT(!"STTime !! CD_STOPPING Fixup with list items\n");
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_NO_INCREMENT, FALSE);
|
||
|
||
}
|
||
|
||
} else if (TEST_FLAG(state, CD_PAUSING)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
||
"STTime !! %x seconds inactivity\n",
|
||
REDBOOK_THREAD_FIXUP_SECONDS));
|
||
|
||
if (IsListEmpty(&deviceExtension->Thread.DigitalList)) {
|
||
|
||
if ((deviceExtension->Thread.PendingRead == 0) &&
|
||
(deviceExtension->Thread.PendingStream == 0) &&
|
||
(deviceExtension->Thread.IoctlCurrent != NULL)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError,
|
||
"[redbook] "
|
||
"STTime !! No Reads, No Streams, In a temp "
|
||
"state (%x) and have PAUSE irp %p "
|
||
"pending?!\n",
|
||
state,
|
||
((PREDBOOK_THREAD_IOCTL_DATA)deviceExtension->Thread.IoctlCurrent)->Irp
|
||
));
|
||
ASSERT(!"STTime !! CD_PAUSING Fixup with no reads nor streams but PAUSE pending\n");
|
||
SetCdromState(deviceExtension, state, CD_PAUSED);
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_COMPLETE],
|
||
IO_NO_INCREMENT, FALSE);
|
||
|
||
} else {
|
||
|
||
ASSERT(!"STTime !! CD_PAUSING Fixup with empty list and no pending ioctl?\n");
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
ASSERT(!"STTime !! CD_PAUSING Fixup with list items\n");
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_NO_INCREMENT, FALSE);
|
||
|
||
}
|
||
|
||
} else if (TEST_FLAG(state, CD_PAUSED)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"STTime => Still paused\n"));
|
||
|
||
} else {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"STTime !! %x seconds inactivity\n",
|
||
REDBOOK_THREAD_FIXUP_SECONDS));
|
||
|
||
if (IsListEmpty(&deviceExtension->Thread.DigitalList)) {
|
||
ASSERT(!"STTime !! CD_PLAYING Fixup with empty list\n");
|
||
} else {
|
||
ASSERT(!"STTime !! CD_PLAYING Fixup with list items\n");
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_NO_INCREMENT, FALSE);
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
}
|
||
|
||
default: {
|
||
|
||
if (waitStatus > 0 && waitStatus < EVENT_MAXIMUM) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
||
"ST !! Unhandled event: %lx\n",
|
||
waitStatus));
|
||
} else {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
||
"ST !! event too large/small: %lx\n",
|
||
waitStatus));
|
||
}
|
||
|
||
ASSERT(!"[redbook] ST !! Unhandled event");
|
||
break;
|
||
}
|
||
|
||
} // end of the huge case statement.
|
||
|
||
//
|
||
// check if ioctl waiting or killed. if so, and state is no longer
|
||
// in an intermediate state, set the appropriate event.
|
||
// ordered to short-curcuit quickly.
|
||
//
|
||
|
||
{
|
||
ULONG state = GetCdromState(deviceExtension);
|
||
|
||
if (killed &&
|
||
deviceExtension->Thread.PendingRead == 0 &&
|
||
deviceExtension->Thread.PendingStream == 0) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"ST => Killing Thread %p\n",
|
||
deviceExtension->Thread.SelfPointer));
|
||
|
||
ASSERT(!TEST_FLAG(state, CD_MASK_TEMP));
|
||
|
||
state = SetCdromState(deviceExtension, state, CD_STOPPED);
|
||
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_KILL_THREAD],
|
||
IO_NO_INCREMENT, FALSE);
|
||
}
|
||
}
|
||
|
||
continue;
|
||
} // while(1) loop
|
||
ASSERT(!"[redbook] ST !! somehow broke out of while(1) loop?");
|
||
}
|
||
|
||
|
||
VOID
|
||
RedBookReadRaw(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
|
||
PREDBOOK_COMPLETION_CONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads raw audio data off the cdrom.
|
||
Must either reinsert Context into queue and set an event
|
||
or set a completion routine which will do so.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - CDROM class driver object or lower level filter
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION nextIrpStack;
|
||
PRAW_READ_INFO readInfo;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalR, "[redbook] "
|
||
"ReadRaw => Entering\n"));
|
||
|
||
status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Context->Irp);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalR, "[redbook] "
|
||
"ReadRaw !! RemoveLock failed %lx\n", status));
|
||
|
||
// end of thread loop will check for no outstanding io
|
||
// and on too many errors set stopping
|
||
// don't forget perf info
|
||
|
||
Context->TimeReadSent.QuadPart = 0; // special value
|
||
Context->Irp->IoStatus.Status = status;
|
||
Context->Reason = REDBOOK_CC_READ_COMPLETE;
|
||
|
||
//
|
||
// put it on the queue and set the event
|
||
//
|
||
|
||
ExInterlockedInsertTailList(&DeviceExtension->Thread.DigitalList,
|
||
&Context->ListEntry,
|
||
&DeviceExtension->Thread.DigitalLock);
|
||
KeSetEvent(DeviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
return;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalR, "[redbook] "
|
||
"ReadRaw => Index %x sending Irp %p\n",
|
||
Context->Index, Context->Irp));
|
||
|
||
//
|
||
// (no failure from this point forward)
|
||
//
|
||
|
||
IoReuseIrp(Context->Irp, STATUS_UNSUCCESSFUL);
|
||
|
||
Context->Irp->MdlAddress = Context->Mdl;
|
||
|
||
//
|
||
// irp is from kernel mode
|
||
//
|
||
|
||
Context->Irp->AssociatedIrp.SystemBuffer = NULL;
|
||
|
||
//
|
||
// fill in the completion context
|
||
//
|
||
|
||
ASSERT(Context->DeviceExtension == DeviceExtension);
|
||
|
||
//
|
||
// setup the irpstack for the raw read
|
||
//
|
||
|
||
nextIrpStack = IoGetNextIrpStackLocation(Context->Irp);
|
||
|
||
SET_FLAG(nextIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
|
||
IOCTL_CDROM_RAW_READ;
|
||
nextIrpStack->Parameters.DeviceIoControl.Type3InputBuffer =
|
||
Context->Buffer;
|
||
nextIrpStack->Parameters.DeviceIoControl.InputBufferLength =
|
||
sizeof(RAW_READ_INFO);
|
||
nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength =
|
||
(RAW_SECTOR_SIZE * DeviceExtension->WmiData.SectorsPerRead);
|
||
|
||
//
|
||
// setup the read info (uses same buffer)
|
||
//
|
||
|
||
readInfo = (PRAW_READ_INFO)(Context->Buffer);
|
||
readInfo->DiskOffset.QuadPart =
|
||
(ULONGLONG)(DeviceExtension->CDRom.NextToRead)*COOKED_SECTOR_SIZE;
|
||
readInfo->SectorCount = DeviceExtension->WmiData.SectorsPerRead;
|
||
readInfo->TrackMode = CDDA;
|
||
|
||
//
|
||
// send it.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Context->Irp, RedBookReadRawCompletion, Context,
|
||
TRUE, TRUE, TRUE);
|
||
KeQueryTickCount(&Context->TimeReadSent);
|
||
IoCallDriver(DeviceExtension->TargetDeviceObject, Context->Irp);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RedBookReadRawCompletion(
|
||
PVOID UnusableParameter,
|
||
PIRP Irp,
|
||
PREDBOOK_COMPLETION_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
When a read completes, use zero'd buffer if error occurred.
|
||
Make buffer available to ks, then set ks event.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - NULL, due to being originator of IRP
|
||
|
||
Irp - pointer to buffer to send to KS
|
||
must check error to increment/clear error count
|
||
|
||
Context - REDBOOK_COMPLETION_CONTEXT
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED
|
||
|
||
--*/
|
||
{
|
||
PREDBOOK_DEVICE_EXTENSION deviceExtension = Context->DeviceExtension;
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalR, "[redbook] "
|
||
"ReadRaw => Completed Irp %p\n", Irp));
|
||
|
||
KeQueryTickCount(&Context->TimeStreamReady);
|
||
Context->Reason = REDBOOK_CC_READ_COMPLETE;
|
||
|
||
ExInterlockedInsertTailList(&deviceExtension->Thread.DigitalList,
|
||
&Context->ListEntry,
|
||
&deviceExtension->Thread.DigitalLock);
|
||
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
|
||
//
|
||
// safe to release it since we wait for thread termination
|
||
//
|
||
|
||
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Context->Irp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
VOID
|
||
RedBookStream(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
|
||
PREDBOOK_COMPLETION_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send buffer to KS.
|
||
Must either reinsert Context into queue and set an event
|
||
or set a completion routine which will do so.
|
||
|
||
Arguments:
|
||
|
||
Context - DeviceExtension
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION nextIrpStack;
|
||
|
||
PUCHAR buffer;
|
||
PKSSTREAM_HEADER header;
|
||
|
||
ULONG bufferSize;
|
||
|
||
PULONG streamOk;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream => Entering\n"));
|
||
|
||
bufferSize = DeviceExtension->WmiData.SectorsPerRead * RAW_SECTOR_SIZE;
|
||
|
||
status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Context->Irp);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream !! RemoveLock failed %lx\n", status));
|
||
|
||
// end of thread loop will check for no outstanding io
|
||
// and on too many errors set CD_STOPPING
|
||
// don't forget perf info
|
||
|
||
Context->TimeReadSent.QuadPart = 0; // special value
|
||
Context->Irp->IoStatus.Status = status;
|
||
Context->Reason = REDBOOK_CC_STREAM_COMPLETE;
|
||
|
||
//
|
||
// put it on the queue and set the event
|
||
//
|
||
|
||
ExInterlockedInsertTailList(&DeviceExtension->Thread.DigitalList,
|
||
&Context->ListEntry,
|
||
&DeviceExtension->Thread.DigitalLock);
|
||
KeSetEvent(DeviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
return;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream => Index %x sending Irp %p\n",
|
||
Context->Index, Context->Irp));
|
||
|
||
//
|
||
// CONSIDER - how does STOPPED occur?
|
||
// SUGGEST - out of loop, if no error and none pending, set to STOPPED?
|
||
//
|
||
|
||
//
|
||
// (no failure from this point forward)
|
||
//
|
||
|
||
//
|
||
// use a zero'd buffer if an error occurred during the read
|
||
//
|
||
|
||
if (NT_SUCCESS(Context->Irp->IoStatus.Status)) {
|
||
IoReuseIrp(Context->Irp, STATUS_SUCCESS);
|
||
buffer = Context->Buffer; // good data
|
||
Context->Irp->MdlAddress = Context->Mdl;
|
||
} else {
|
||
IoReuseIrp(Context->Irp, STATUS_SUCCESS);
|
||
buffer = DeviceExtension->Buffer.SilentBuffer; // zero'd data
|
||
Context->Irp->MdlAddress = DeviceExtension->Buffer.SilentMdl;
|
||
}
|
||
|
||
#if DBG
|
||
if (RedBookForceSilence) {
|
||
buffer = DeviceExtension->Buffer.SilentBuffer; // zero'd data
|
||
Context->Irp->MdlAddress = DeviceExtension->Buffer.SilentMdl;
|
||
}
|
||
#endif // RedBookUseSilence
|
||
|
||
|
||
nextIrpStack = IoGetNextIrpStackLocation(Context->Irp);
|
||
|
||
//
|
||
// get and fill in the context
|
||
//
|
||
|
||
ASSERT(Context->DeviceExtension == DeviceExtension);
|
||
|
||
//
|
||
// setup the irpstack for streaming the buffer
|
||
//
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
|
||
IOCTL_KS_WRITE_STREAM;
|
||
nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength =
|
||
sizeof(KSSTREAM_HEADER);
|
||
nextIrpStack->Parameters.DeviceIoControl.InputBufferLength =
|
||
sizeof(KSSTREAM_HEADER);
|
||
nextIrpStack->FileObject = DeviceExtension->Stream.PinFileObject;
|
||
|
||
Context->Header.FrameExtent = bufferSize;
|
||
Context->Header.DataUsed = bufferSize;
|
||
Context->Header.Size = sizeof(KSSTREAM_HEADER);
|
||
Context->Header.TypeSpecificFlags = 0;
|
||
Context->Header.Data = buffer;
|
||
Context->Header.OptionsFlags = 0;
|
||
|
||
Context->Irp->AssociatedIrp.SystemBuffer = &Context->Header;
|
||
|
||
|
||
|
||
#if REDBOOK_PERFORM_STUTTER_CONTROL
|
||
|
||
#if REDBOOK_WMI_BUFFERS_MIN < 3
|
||
#error "The minimum number of buffers must be at least three due to the method used to prevent stuttering"
|
||
#endif // REDBOOK_WMI_BUFFERS_MIN < 3
|
||
|
||
//
|
||
// perform my own pausing to prevent stuttering
|
||
//
|
||
|
||
if (DeviceExtension->Thread.PendingStream <= 3 &&
|
||
DeviceExtension->Buffer.Paused == 0) {
|
||
|
||
//
|
||
// only one buffer (or less) was pending play,
|
||
// so pause the output to prevent horrible
|
||
// stuttering.
|
||
// since this is serialized from a thread,
|
||
// can set a simple boolean in the extension
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream => Pausing, few buffers pending\n"));
|
||
|
||
if (DeviceExtension->Buffer.FirstPause == 0) {
|
||
RedBookLogError(DeviceExtension,
|
||
REDBOOK_ERR_INSUFFICIENT_DATA_STREAM_PAUSED,
|
||
STATUS_SUCCESS
|
||
);
|
||
InterlockedIncrement(&DeviceExtension->WmiPerf.StreamPausedCount);
|
||
} else {
|
||
DeviceExtension->Buffer.FirstPause = 0;
|
||
}
|
||
|
||
DeviceExtension->Buffer.Paused = 1;
|
||
SetNextDeviceState(DeviceExtension, KSSTATE_PAUSE);
|
||
|
||
} else if (DeviceExtension->Buffer.Paused == 1 &&
|
||
DeviceExtension->Thread.PendingStream ==
|
||
DeviceExtension->WmiData.NumberOfBuffers ) {
|
||
|
||
ULONG i;
|
||
|
||
//
|
||
// are now using the maximum number of buffers,
|
||
// all pending stream. this allows smooth play again.
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream => Resuming, %d buffers pending\n",
|
||
DeviceExtension->WmiData.NumberOfBuffers));
|
||
DeviceExtension->Buffer.Paused = 0;
|
||
|
||
//
|
||
// prevent these statistics from being added.
|
||
//
|
||
|
||
for (i=0;i<DeviceExtension->WmiData.NumberOfBuffers;i++) {
|
||
(DeviceExtension->Buffer.Contexts + i)->TimeReadSent.QuadPart = 0;
|
||
}
|
||
|
||
//
|
||
// let the irps go!
|
||
//
|
||
|
||
SetNextDeviceState(DeviceExtension, KSSTATE_RUN);
|
||
|
||
} // end of stutter prevention
|
||
#endif // REDBOOK_PERFORM_STUTTER_CONTROL
|
||
|
||
//
|
||
// get perf counters at last possible second
|
||
//
|
||
|
||
KeQueryTickCount(&Context->TimeStreamSent);
|
||
IoSetCompletionRoutine(Context->Irp, RedBookStreamCompletion, Context,
|
||
TRUE, TRUE, TRUE);
|
||
IoCallDriver(DeviceExtension->Stream.PinDeviceObject, Context->Irp);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RedBookStreamCompletion(
|
||
PVOID UnusableParameter,
|
||
PIRP Irp,
|
||
PREDBOOK_COMPLETION_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - CDROM class driver object or lower level filter
|
||
|
||
Irp - pointer to buffer to send to KS
|
||
must check error to increment/clear error count
|
||
|
||
Context - sector of disk (ordered number)
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PREDBOOK_DEVICE_EXTENSION deviceExtension = Context->DeviceExtension;
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugDigitalS, "[redbook] "
|
||
"Stream => Completed Irp %p\n", Irp));
|
||
|
||
KeQueryTickCount(&Context->TimeReadReady);
|
||
Context->Reason = REDBOOK_CC_STREAM_COMPLETE;
|
||
|
||
ExInterlockedInsertTailList(&deviceExtension->Thread.DigitalList,
|
||
&Context->ListEntry,
|
||
&deviceExtension->Thread.DigitalLock);
|
||
|
||
KeSetEvent(deviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
|
||
//
|
||
// safe to release it since we wait for thread termination
|
||
//
|
||
|
||
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Context->Irp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
#if DBG
|
||
VOID
|
||
ValidateCdromState(ULONG State)
|
||
{
|
||
ULONG temp;
|
||
|
||
if (State == 0) {
|
||
ASSERT(!"Invalid Cdrom State");
|
||
} else
|
||
if (TEST_FLAG(State, ~CD_MASK_ALL)) {
|
||
ASSERT(!"Invalid Cdrom State");
|
||
}
|
||
|
||
temp = State & CD_MASK_TEMP;
|
||
if (temp & (temp - 1)) { // see if zero or one bits are set
|
||
ASSERT(!"Invalid Cdrom State");
|
||
}
|
||
|
||
temp = State & CD_MASK_STATE;
|
||
if (temp == 0) { // dis-allow zero bits for STATE
|
||
ASSERT(!"Invalid Cdrom State");
|
||
} else
|
||
if (temp & (temp - 1)) { // see if zero or one bits are set
|
||
ASSERT(!"Invalid Cdrom State");
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
#else
|
||
VOID ValidateCdromState(ULONG State) {return;}
|
||
#endif
|
||
|
||
|
||
ULONG
|
||
GetCdromState(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
//
|
||
// this routine may be called by anyone, whether in the thread's
|
||
// context or not. setting the state is restricted, however.
|
||
//
|
||
ULONG state;
|
||
state = InterlockedCompareExchange(&DeviceExtension->CDRom.StateNow,0,0);
|
||
ValidateCdromState(state);
|
||
return state;
|
||
}
|
||
|
||
|
||
LONG
|
||
SetCdromState(
|
||
IN PREDBOOK_DEVICE_EXTENSION DeviceExtension,
|
||
IN LONG ExpectedOldState,
|
||
IN LONG NewState
|
||
)
|
||
{
|
||
LONG trueOldState;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
// ensure when set to: also setting:
|
||
// CD_PAUSING CD_PLAYING
|
||
// CD_STOPPING CD_PLAYING
|
||
|
||
if (TEST_FLAG(NewState, CD_PAUSING)) {
|
||
SET_FLAG(NewState, CD_PLAYING);
|
||
}
|
||
|
||
if (TEST_FLAG(NewState, CD_STOPPING)) {
|
||
SET_FLAG(NewState, CD_PLAYING);
|
||
}
|
||
|
||
ValidateCdromState(ExpectedOldState);
|
||
ValidateCdromState(NewState);
|
||
|
||
//attempt to change it
|
||
trueOldState = InterlockedCompareExchange(
|
||
&DeviceExtension->CDRom.StateNow,
|
||
NewState,
|
||
ExpectedOldState
|
||
);
|
||
|
||
ASSERTMSG("State set outside of thread",
|
||
trueOldState == ExpectedOldState);
|
||
|
||
//
|
||
// see if an event should be fired, volume set, and/or
|
||
// stream state set
|
||
//
|
||
if (ExpectedOldState == NewState) {
|
||
|
||
//
|
||
// if state is not changing, don't do anything
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Setting state to same as expected?! %x == %x\n",
|
||
ExpectedOldState, NewState));
|
||
|
||
} else if (TEST_FLAG(ExpectedOldState, CD_MASK_TEMP)) {
|
||
|
||
//
|
||
// should not go from temp state to temp state
|
||
//
|
||
|
||
ASSERT(!TEST_FLAG(NewState, CD_MASK_TEMP));
|
||
|
||
//
|
||
// ioctl is being processed, and state is no longer
|
||
// in a temp state, so should process the ioctl again
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"SetState => EVENT_COMPLETE should be set soon "
|
||
"for %p\n", DeviceExtension->Thread.IoctlCurrent));
|
||
|
||
} else if (TEST_FLAG(NewState, CD_MASK_TEMP)) {
|
||
|
||
//
|
||
// going to either pausing or stopping, both of which must
|
||
// be specially handled by stopping the KS stream also.
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"SetState => %s, setting device state "
|
||
"to KSSTATE_STOP\n",
|
||
(TEST_FLAG(NewState, CD_STOPPING) ? "STOP" : "PAUSE")));
|
||
|
||
SetNextDeviceState(DeviceExtension, KSSTATE_STOP);
|
||
|
||
} else if (TEST_FLAG(NewState, CD_PAUSED)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"SetState => Finishing a PAUSE operation\n"));
|
||
|
||
} else if (TEST_FLAG(NewState, CD_PLAYING)) {
|
||
|
||
ULONG i;
|
||
PREDBOOK_COMPLETION_CONTEXT context;
|
||
|
||
ASSERT(NewState == CD_PLAYING);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"SetState => Starting a PLAY operation\n"));
|
||
|
||
//
|
||
// not same state, not from temp state,
|
||
// so must either be paused or stopped.
|
||
//
|
||
|
||
ASSERT(TEST_FLAG(ExpectedOldState,CD_STOPPED) ||
|
||
TEST_FLAG(ExpectedOldState,CD_PAUSED));
|
||
|
||
//
|
||
// set some deviceextension stuff
|
||
//
|
||
|
||
|
||
RtlZeroMemory(&DeviceExtension->WmiPerf,
|
||
sizeof(REDBOOK_WMI_PERF_DATA));
|
||
DeviceExtension->CDRom.ReadErrors = 0;
|
||
DeviceExtension->CDRom.StreamErrors = 0;
|
||
DeviceExtension->Buffer.Paused = 0;
|
||
DeviceExtension->Buffer.FirstPause = 1;
|
||
DeviceExtension->Buffer.IndexToRead = 0;
|
||
DeviceExtension->Buffer.IndexToStream = 0;
|
||
|
||
//
|
||
// reset the buffer state
|
||
//
|
||
|
||
ASSERT(DeviceExtension->Buffer.Contexts);
|
||
context = DeviceExtension->Buffer.Contexts;
|
||
|
||
for (i=0; i<DeviceExtension->WmiData.NumberOfBuffers;i++) {
|
||
|
||
*(DeviceExtension->Buffer.ReadOk_X + i) = 0;
|
||
*(DeviceExtension->Buffer.StreamOk_X + i) = 0;
|
||
|
||
context->Reason = REDBOOK_CC_READ;
|
||
context->Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
ExInterlockedInsertTailList(&DeviceExtension->Thread.DigitalList,
|
||
&context->ListEntry,
|
||
&DeviceExtension->Thread.DigitalLock);
|
||
|
||
context++; // pointer arithmetic
|
||
}
|
||
context = NULL;
|
||
|
||
//
|
||
// start the digital playback
|
||
//
|
||
|
||
SetNextDeviceState(DeviceExtension, KSSTATE_RUN);
|
||
RedBookKsSetVolume(DeviceExtension);
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"SetState => Setting DIGITAL event\n"));
|
||
KeSetEvent(DeviceExtension->Thread.Events[EVENT_DIGITAL],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// ReadQ Channel or some such nonsense
|
||
//
|
||
|
||
}
|
||
|
||
|
||
return GetCdromState(DeviceExtension);
|
||
}
|
||
|
||
|
||
VOID
|
||
RedBookDeallocatePlayResources(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
PREDBOOK_COMPLETION_CONTEXT context;
|
||
ULONG i;
|
||
BOOLEAN freedSomething = FALSE;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
#if DBG
|
||
{
|
||
ULONG state = GetCdromState(DeviceExtension);
|
||
ASSERT(!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED));
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// free all resources
|
||
//
|
||
|
||
if (DeviceExtension->Buffer.StreamOk_X) {
|
||
freedSomething = TRUE;
|
||
ExFreePool(DeviceExtension->Buffer.StreamOk_X);
|
||
DeviceExtension->Buffer.StreamOk_X = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Buffer.ReadOk_X) {
|
||
freedSomething = TRUE;
|
||
ExFreePool(DeviceExtension->Buffer.ReadOk_X);
|
||
DeviceExtension->Buffer.ReadOk_X = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Buffer.Contexts) {
|
||
|
||
context = DeviceExtension->Buffer.Contexts;
|
||
for (i=0;i<DeviceExtension->WmiData.NumberOfBuffers;i++) {
|
||
if (context->Irp) {
|
||
IoFreeIrp(context->Irp);
|
||
}
|
||
if (context->Mdl) {
|
||
IoFreeMdl(context->Mdl);
|
||
}
|
||
context++; // pointer arithmetic
|
||
}
|
||
context = NULL;
|
||
|
||
freedSomething = TRUE;
|
||
ExFreePool(DeviceExtension->Buffer.Contexts);
|
||
DeviceExtension->Buffer.Contexts = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Buffer.SilentMdl) {
|
||
freedSomething = TRUE;
|
||
IoFreeMdl(DeviceExtension->Buffer.SilentMdl);
|
||
DeviceExtension->Buffer.SilentMdl = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Buffer.SkipBuffer) {
|
||
freedSomething = TRUE;
|
||
ExFreePool(DeviceExtension->Buffer.SkipBuffer);
|
||
DeviceExtension->Buffer.SkipBuffer = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Thread.CheckVerifyIrp) {
|
||
PIRP irp = DeviceExtension->Thread.CheckVerifyIrp;
|
||
freedSomething = TRUE;
|
||
if (irp->MdlAddress) {
|
||
IoFreeMdl(irp->MdlAddress);
|
||
}
|
||
if (irp->AssociatedIrp.SystemBuffer) {
|
||
ExFreePool(irp->AssociatedIrp.SystemBuffer);
|
||
}
|
||
|
||
IoFreeIrp(DeviceExtension->Thread.CheckVerifyIrp);
|
||
DeviceExtension->Thread.CheckVerifyIrp = NULL;
|
||
}
|
||
|
||
if (DeviceExtension->Stream.PinFileObject) {
|
||
freedSomething = TRUE;
|
||
CloseSysAudio(DeviceExtension);
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.Toc) {
|
||
freedSomething = TRUE;
|
||
ExFreePool(DeviceExtension->CDRom.Toc);
|
||
DeviceExtension->CDRom.Toc = NULL;
|
||
}
|
||
|
||
if (freedSomething) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"DeallocatePlay => Deallocated play resources\n"));
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
RedBookArePlayResourcesAllocated(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
//
|
||
// just choose one, since it's all done in a batch in
|
||
// one thread context it's always safe.
|
||
//
|
||
{
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
return (DeviceExtension->Stream.PinFileObject != NULL);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RedBookAllocatePlayResources(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
//
|
||
// allocate resources if they are not already allocated
|
||
//
|
||
{
|
||
PREDBOOK_COMPLETION_CONTEXT context;
|
||
NTSTATUS status;
|
||
KEVENT event;
|
||
ULONG numBufs;
|
||
ULONG numSectors;
|
||
ULONG bufSize;
|
||
ULONG i;
|
||
CCHAR maxStack;
|
||
BOOLEAN sysAudioOpened = FALSE;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
//
|
||
// NOTE:
|
||
// The call to update the mixer Id may de-allocate all play
|
||
// resources, since the stack sizes may change. it must
|
||
// therefore be the first call within this routine.
|
||
//
|
||
|
||
if (DeviceExtension->Stream.MixerPinId == -1) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No mixer set?\n"));
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if (DeviceExtension->Buffer.Contexts != NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => Using existing resources\n"));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#if DBG
|
||
{
|
||
ULONG state = GetCdromState(DeviceExtension);
|
||
ASSERT(!TEST_FLAG(state, CD_PLAYING) && !TEST_FLAG(state, CD_PAUSED));
|
||
}
|
||
#endif
|
||
|
||
ASSERT(DeviceExtension->Buffer.Contexts == NULL);
|
||
ASSERT(DeviceExtension->Buffer.SkipBuffer == NULL);
|
||
|
||
numBufs = DeviceExtension->WmiData.NumberOfBuffers;
|
||
numSectors = DeviceExtension->WmiData.SectorsPerRead;
|
||
bufSize = RAW_SECTOR_SIZE * numSectors;
|
||
|
||
TRY {
|
||
|
||
ASSERT(DeviceExtension->Stream.MixerPinId != -1);
|
||
|
||
//
|
||
// may need to allocate the CheckVerifyIrp
|
||
//
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpStack;
|
||
PIRP irp;
|
||
irp = DeviceExtension->Thread.CheckVerifyIrp;
|
||
|
||
if (irp == NULL) {
|
||
irp = IoAllocateIrp(
|
||
(CCHAR)(DeviceExtension->SelfDeviceObject->StackSize+1),
|
||
FALSE);
|
||
}
|
||
if (irp == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No CheckVerifyIrp\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
irp->AssociatedIrp.SystemBuffer = irp->MdlAddress = NULL;
|
||
|
||
irp->AssociatedIrp.SystemBuffer =
|
||
ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(ULONG),
|
||
TAG_CV_BUFFER);
|
||
if (irp->AssociatedIrp.SystemBuffer == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No CheckVerify Buffer\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
irp->MdlAddress = IoAllocateMdl(irp->AssociatedIrp.SystemBuffer,
|
||
sizeof(ULONG),
|
||
FALSE, FALSE, NULL);
|
||
if (irp->MdlAddress == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No CheckVerify Mdl\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
MmBuildMdlForNonPagedPool(irp->MdlAddress);
|
||
|
||
IoSetNextIrpStackLocation(irp);
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
|
||
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
|
||
irpStack->Parameters.DeviceIoControl.InputBufferLength =
|
||
0;
|
||
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
|
||
sizeof(ULONG);
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode =
|
||
IOCTL_CDROM_CHECK_VERIFY;
|
||
|
||
DeviceExtension->Thread.CheckVerifyIrp = irp;
|
||
}
|
||
|
||
|
||
//
|
||
// connect to sysaudio
|
||
//
|
||
|
||
{
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => Preparing to open sysaudio\n"));
|
||
|
||
ASSERT(DeviceExtension->Stream.MixerPinId != MAXULONG);
|
||
|
||
status = OpenSysAudio(DeviceExtension);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugWarning, "[redbook] "
|
||
"AllocatePlay !! Unable to open sysaudio %lx\n",
|
||
status));
|
||
LEAVE;
|
||
}
|
||
|
||
// else the pin is open
|
||
sysAudioOpened = TRUE;
|
||
}
|
||
|
||
maxStack = MAX(DeviceExtension->TargetDeviceObject->StackSize,
|
||
DeviceExtension->Stream.PinDeviceObject->StackSize);
|
||
DeviceExtension->Buffer.MaxIrpStack = maxStack;
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocateePlay => Stacks: Cdrom %x Stream %x\n",
|
||
DeviceExtension->TargetDeviceObject->StackSize,
|
||
DeviceExtension->Stream.PinDeviceObject->StackSize));
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocateePlay => Allocating %x stacks per irp\n",
|
||
maxStack));
|
||
|
||
|
||
DeviceExtension->Buffer.SkipBuffer =
|
||
ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
bufSize * (numBufs + 1),
|
||
TAG_BUFFER);
|
||
|
||
if (DeviceExtension->Buffer.SkipBuffer == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No Skipbuffer\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
RtlZeroMemory(DeviceExtension->Buffer.SkipBuffer,
|
||
bufSize * (numBufs + 1));
|
||
|
||
DeviceExtension->Buffer.Contexts =
|
||
ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(REDBOOK_COMPLETION_CONTEXT) * numBufs,
|
||
TAG_CC);
|
||
|
||
if (DeviceExtension->Buffer.Contexts == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => No Contexts\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
RtlZeroMemory(DeviceExtension->Buffer.Contexts,
|
||
sizeof(REDBOOK_COMPLETION_CONTEXT) * numBufs);
|
||
|
||
context = DeviceExtension->Buffer.Contexts;
|
||
for (i=0;i<numBufs;i++) {
|
||
|
||
context->DeviceExtension = DeviceExtension;
|
||
context->Reason = REDBOOK_CC_READ;
|
||
context->Index = i;
|
||
context->Buffer = DeviceExtension->Buffer.SkipBuffer +
|
||
(bufSize * i); // pointer arithmetic of UCHARS
|
||
|
||
//
|
||
// allocate irp, mdl
|
||
//
|
||
|
||
context->Irp = IoAllocateIrp(maxStack, FALSE);
|
||
context->Mdl = IoAllocateMdl(context->Buffer, bufSize,
|
||
FALSE, FALSE, NULL);
|
||
if (context->Irp == NULL ||
|
||
context->Mdl == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => Irp/Mdl %x failed\n", i));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
MmBuildMdlForNonPagedPool(context->Mdl);
|
||
|
||
context++; // pointer arithmetic of CONTEXTS
|
||
}
|
||
context = NULL; // safety
|
||
|
||
//
|
||
// allocated above as part of SkipBuffer
|
||
//
|
||
|
||
DeviceExtension->Buffer.SilentBuffer =
|
||
DeviceExtension->Buffer.SkipBuffer + (bufSize * numBufs);
|
||
|
||
|
||
DeviceExtension->Buffer.SilentMdl =
|
||
IoAllocateMdl(DeviceExtension->Buffer.SkipBuffer, bufSize,
|
||
FALSE, FALSE, NULL);
|
||
if (DeviceExtension->Buffer.SilentMdl == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => Silent Mdl failed\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
|
||
DeviceExtension->Buffer.ReadOk_X =
|
||
ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(ULONG) * numBufs,
|
||
TAG_READX);
|
||
if (DeviceExtension->Buffer.ReadOk_X == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => ReadOk_X failed\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
RtlZeroMemory(DeviceExtension->Buffer.ReadOk_X,
|
||
sizeof(ULONG) * numBufs);
|
||
|
||
DeviceExtension->Buffer.StreamOk_X =
|
||
ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(ULONG) * numBufs,
|
||
TAG_STREAMX);
|
||
if (DeviceExtension->Buffer.StreamOk_X == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => ReadOk_X failed\n"));
|
||
status = STATUS_NO_MEMORY;
|
||
LEAVE;
|
||
}
|
||
RtlZeroMemory(DeviceExtension->Buffer.StreamOk_X,
|
||
sizeof(ULONG) * numBufs);
|
||
|
||
MmBuildMdlForNonPagedPool(DeviceExtension->Buffer.SilentMdl);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"AllocatePlay => Allocated All Resources\n"));
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} FINALLY {
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
RedBookDeallocatePlayResources(DeviceExtension);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// else all resources allocated
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RedBookCacheToc(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
PCDROM_TOC newToc;
|
||
PIRP irp;
|
||
ULONG mediaChangeCount;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
KEVENT event;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
//
|
||
// cache the number of times the media has changed
|
||
// use this to prevent redundant reads of the toc
|
||
// and to return Q channel info during playback
|
||
//
|
||
|
||
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
||
|
||
//
|
||
// first get the mediaChangeCount to see if we've already
|
||
// cached this toc
|
||
//
|
||
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_CHECK_VERIFY,
|
||
DeviceExtension->TargetDeviceObject,
|
||
NULL, 0,
|
||
&mediaChangeCount, sizeof(ULONG),
|
||
FALSE,
|
||
&event, &ioStatus);
|
||
if (irp == NULL) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
SET_FLAG(IoGetNextIrpStackLocation(irp)->Flags, SL_OVERRIDE_VERIFY_VOLUME);
|
||
|
||
status = IoCallDriver(DeviceExtension->TargetDeviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
||
status = ioStatus.Status;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"CacheToc !! CheckVerify failed %lx\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// read TOC only we don't have the correct copy cached
|
||
//
|
||
|
||
if (DeviceExtension->CDRom.Toc != NULL &&
|
||
DeviceExtension->CDRom.CheckVerify == mediaChangeCount) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"CacheToc => Using cached toc\n"));
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate for the cached TOC
|
||
//
|
||
|
||
newToc = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(CDROM_TOC),
|
||
TAG_TOC);
|
||
|
||
if (newToc == NULL) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"CacheToc !! Unable to allocate new TOC\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
KeClearEvent(&event);
|
||
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_TOC,
|
||
DeviceExtension->TargetDeviceObject,
|
||
NULL, 0,
|
||
newToc, sizeof(CDROM_TOC),
|
||
FALSE,
|
||
&event, &ioStatus);
|
||
if (irp == NULL) {
|
||
ExFreePool(newToc);
|
||
newToc = NULL;
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
SET_FLAG(IoGetNextIrpStackLocation(irp)->Flags, SL_OVERRIDE_VERIFY_VOLUME);
|
||
|
||
status = IoCallDriver(DeviceExtension->TargetDeviceObject, irp);
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
||
status = ioStatus.Status;
|
||
}
|
||
|
||
//
|
||
// set the new toc, or if error free it
|
||
// return the status
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ExFreePool(newToc);
|
||
newToc = NULL;
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"CacheToc !! Failed to get TOC %lx\n", status));
|
||
|
||
} else {
|
||
|
||
if (DeviceExtension->CDRom.Toc) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugAllocPlay, "[redbook] "
|
||
"CacheToc => Freeing old toc %p\n",
|
||
DeviceExtension->CDRom.Toc));
|
||
ExFreePool(DeviceExtension->CDRom.Toc);
|
||
}
|
||
DeviceExtension->CDRom.Toc = newToc;
|
||
DeviceExtension->CDRom.CheckVerify = mediaChangeCount;
|
||
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
RedBookThreadDigitalHandler(
|
||
IN PREDBOOK_DEVICE_EXTENSION DeviceExtension,
|
||
IN PLIST_ENTRY ListEntry
|
||
)
|
||
//
|
||
// DECREMENT StreamPending/ReadPending if it's a completion
|
||
// SET stopped, error, etc. states
|
||
// INCREMENT StreamPending/ReadPending if it's to be sent again
|
||
//
|
||
{
|
||
PREDBOOK_COMPLETION_CONTEXT Context;
|
||
ULONG index;
|
||
ULONG mod;
|
||
ULONG state;
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
ASSERT(DeviceExtension->WmiData.NumberOfBuffers);
|
||
ASSERT(DeviceExtension->Buffer.SkipBuffer);
|
||
|
||
//
|
||
// Increment/Decrement PendingRead/PendingStream
|
||
//
|
||
|
||
Context = CONTAINING_RECORD(ListEntry, REDBOOK_COMPLETION_CONTEXT, ListEntry);
|
||
|
||
index = Context->Index;
|
||
mod = DeviceExtension->WmiData.NumberOfBuffers;
|
||
|
||
state = GetCdromState(DeviceExtension);
|
||
|
||
//
|
||
// decrement the number reading/streaming if needed
|
||
//
|
||
|
||
if (Context->Reason == REDBOOK_CC_READ_COMPLETE) {
|
||
|
||
if (!NT_SUCCESS(Context->Irp->IoStatus.Status)) {
|
||
|
||
if (IoIsErrorUserInduced(Context->Irp->IoStatus.Status)) {
|
||
|
||
DeviceExtension->CDRom.ReadErrors = REDBOOK_MAX_CONSECUTIVE_ERRORS;
|
||
|
||
} else {
|
||
|
||
DeviceExtension->CDRom.ReadErrors++;
|
||
}
|
||
|
||
} else {
|
||
DeviceExtension->CDRom.ReadErrors = 0;
|
||
}
|
||
|
||
DeviceExtension->Thread.PendingRead--;
|
||
Context->Reason = REDBOOK_CC_STREAM;
|
||
|
||
} else if (Context->Reason == REDBOOK_CC_STREAM_COMPLETE) {
|
||
|
||
if (!NT_SUCCESS(Context->Irp->IoStatus.Status)) {
|
||
DeviceExtension->CDRom.StreamErrors++;
|
||
} else {
|
||
DeviceExtension->CDRom.StreamErrors = 0;
|
||
}
|
||
|
||
|
||
//
|
||
// if stream succeeded OR we are _NOT_ stopping audio,
|
||
// increment FinishedStreaming and save wmi stats
|
||
//
|
||
|
||
if (NT_SUCCESS(Context->Irp->IoStatus.Status) ||
|
||
!TEST_FLAG(state, CD_MASK_TEMP)) {
|
||
|
||
DeviceExtension->CDRom.FinishedStreaming +=
|
||
DeviceExtension->WmiData.SectorsPerRead;
|
||
|
||
AddWmiStats(DeviceExtension, Context);
|
||
|
||
}
|
||
|
||
DeviceExtension->Thread.PendingStream--;
|
||
Context->Reason = REDBOOK_CC_READ;
|
||
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.StreamErrors >= REDBOOK_MAX_CONSECUTIVE_ERRORS &&
|
||
!TEST_FLAG(state, CD_MASK_TEMP)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => Too many stream errors, beginning STOP\n"));
|
||
ASSERT(!TEST_FLAG(state, CD_STOPPED));
|
||
ASSERT(!TEST_FLAG(state, CD_PAUSED));
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPING);
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.ReadErrors >= REDBOOK_MAX_CONSECUTIVE_ERRORS &&
|
||
!TEST_FLAG(state, CD_MASK_TEMP)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => Too many read errors, beginning STOP\n"));
|
||
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPING);
|
||
}
|
||
|
||
//
|
||
// if stopping/pausing/etc, and no reads/streams are pending,
|
||
// set the new state and return.
|
||
// the while() loop in the thread will do the right thing
|
||
// when there is no more outstanding io--it will call the ioctl
|
||
// completion handler to do whatever post-processing is needed.
|
||
//
|
||
|
||
if (TEST_FLAG(state, CD_MASK_TEMP)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => Temp state %x, not continuing (%d, %d)\n",
|
||
state,
|
||
DeviceExtension->Thread.PendingRead,
|
||
DeviceExtension->Thread.PendingStream
|
||
));
|
||
|
||
if (DeviceExtension->Thread.PendingRead == 0 &&
|
||
DeviceExtension->Thread.PendingStream == 0) {
|
||
|
||
//
|
||
// Set NextToRead and NextToStream to FinishedStreaming
|
||
//
|
||
|
||
DeviceExtension->CDRom.NextToRead =
|
||
DeviceExtension->CDRom.NextToStream =
|
||
DeviceExtension->CDRom.FinishedStreaming;
|
||
|
||
if (TEST_FLAG(state, CD_PAUSING)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => completing PAUSED\n"));
|
||
state = SetCdromState(DeviceExtension, state, CD_PAUSED);
|
||
} else if (TEST_FLAG(state, CD_STOPPING)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => completing STOPPED\n"));
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
|
||
} else {
|
||
ASSERT(!"Unknown state?");
|
||
}
|
||
|
||
if (DeviceExtension->Thread.IoctlCurrent) {
|
||
KeSetEvent(DeviceExtension->Thread.Events[EVENT_COMPLETE],
|
||
IO_CD_ROM_INCREMENT, FALSE);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.NextToRead >= DeviceExtension->CDRom.EndPlay &&
|
||
Context->Reason == REDBOOK_CC_READ) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => End play, ignoring READ\n"));
|
||
if (DeviceExtension->Thread.PendingRead == 0 &&
|
||
DeviceExtension->Thread.PendingStream == 0) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => All IO done, setting STOPPED\n"));
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.NextToStream >= DeviceExtension->CDRom.EndPlay &&
|
||
Context->Reason == REDBOOK_CC_STREAM) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => End play, ignoring STREAM\n"));
|
||
if (DeviceExtension->Thread.PendingRead == 0 &&
|
||
DeviceExtension->Thread.PendingStream == 0) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => All IO done, setting STOPPED\n"));
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
|
||
|
||
}
|
||
return;
|
||
}
|
||
|
||
switch(Context->Reason) {
|
||
|
||
case REDBOOK_CC_READ: {
|
||
|
||
// mark this buffer as off the queue/usable
|
||
ASSERT(DeviceExtension->Buffer.ReadOk_X[index] == 0);
|
||
DeviceExtension->Buffer.ReadOk_X[index] = 1;
|
||
|
||
if (index != DeviceExtension->Buffer.IndexToRead) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => Delaying read, index %x\n", index));
|
||
return;
|
||
}
|
||
|
||
if (DeviceExtension->CDRom.NextToRead >
|
||
DeviceExtension->CDRom.EndPlay) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Digital => End of Play\n"));
|
||
return;
|
||
}
|
||
|
||
for (index = Context->Index;
|
||
DeviceExtension->Buffer.ReadOk_X[index] != 0;
|
||
index = (index + 1) % mod) {
|
||
|
||
// mark this buffer as in use BEFORE attempting to read
|
||
DeviceExtension->Buffer.ReadOk_X[index] = 0;
|
||
DeviceExtension->Thread.PendingRead++;
|
||
|
||
RedBookReadRaw(DeviceExtension,
|
||
&DeviceExtension->Buffer.Contexts[index]);
|
||
|
||
// increment where reading from AFTER attempting to read
|
||
DeviceExtension->CDRom.NextToRead +=
|
||
DeviceExtension->WmiData.SectorsPerRead;
|
||
|
||
// inc/mod the index AFTER attempting to read
|
||
DeviceExtension->Buffer.IndexToRead++;
|
||
DeviceExtension->Buffer.IndexToRead %= mod;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case REDBOOK_CC_STREAM: {
|
||
|
||
// mark this buffer as off the queue/usable
|
||
ASSERT(DeviceExtension->Buffer.StreamOk_X[index] == 0);
|
||
DeviceExtension->Buffer.StreamOk_X[index] = 1;
|
||
|
||
if (index != DeviceExtension->Buffer.IndexToStream) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugThread, "[redbook] "
|
||
"Delaying stream of index %x\n", index));
|
||
return;
|
||
}
|
||
|
||
for (index = Context->Index;
|
||
DeviceExtension->Buffer.StreamOk_X[index] != 0;
|
||
index = (index + 1) % mod) {
|
||
|
||
// mark this buffer as in use BEFORE attempting to read
|
||
DeviceExtension->Buffer.StreamOk_X[index] = 0;
|
||
DeviceExtension->Thread.PendingStream++;
|
||
|
||
RedBookStream(DeviceExtension,
|
||
&DeviceExtension->Buffer.Contexts[index]);
|
||
|
||
// increment where reading from AFTER attempting to read
|
||
DeviceExtension->CDRom.NextToStream +=
|
||
DeviceExtension->WmiData.SectorsPerRead;
|
||
|
||
// inc/mod the index AFTER attempting to read
|
||
DeviceExtension->Buffer.IndexToStream++;
|
||
DeviceExtension->Buffer.IndexToStream %= mod;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
ASSERT(!"Unhandled Context->Reason\n");
|
||
break;
|
||
}
|
||
|
||
} // end switch (Context->Reason)
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
AddWmiStats(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension,
|
||
PREDBOOK_COMPLETION_CONTEXT Context
|
||
)
|
||
{
|
||
KIRQL oldIrql;
|
||
ULONG timeIncrement;
|
||
|
||
if (Context->TimeReadSent.QuadPart == 0) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugWmi, "[redbook] "
|
||
"Not Saving WMI Stats for REASON:\n"));
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugWmi, "[redbook] "
|
||
"(ReadError, StreamError, Paused?)\n"));
|
||
return;
|
||
}
|
||
|
||
timeIncrement = KeQueryTimeIncrement(); // amount of time for each tick
|
||
|
||
KeAcquireSpinLock(&DeviceExtension->WmiPerfLock, &oldIrql);
|
||
|
||
DeviceExtension->WmiPerf.TimeReadDelay +=
|
||
(Context->TimeReadSent.QuadPart -
|
||
Context->TimeReadReady.QuadPart ) *
|
||
timeIncrement;
|
||
DeviceExtension->WmiPerf.TimeReading +=
|
||
(Context->TimeStreamReady.QuadPart -
|
||
Context->TimeReadSent.QuadPart ) *
|
||
timeIncrement;
|
||
DeviceExtension->WmiPerf.TimeStreamDelay +=
|
||
(Context->TimeStreamSent.QuadPart -
|
||
Context->TimeStreamReady.QuadPart ) *
|
||
timeIncrement;
|
||
DeviceExtension->WmiPerf.TimeStreaming +=
|
||
(Context->TimeReadReady.QuadPart -
|
||
Context->TimeStreamSent.QuadPart ) *
|
||
timeIncrement;
|
||
|
||
DeviceExtension->WmiPerf.DataProcessed +=
|
||
DeviceExtension->WmiData.SectorsPerRead * RAW_SECTOR_SIZE;
|
||
|
||
KeReleaseSpinLock( &DeviceExtension->WmiPerfLock, oldIrql );
|
||
return;
|
||
}
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
VOID
|
||
RedBookCheckForAudioDeviceRemoval(
|
||
PREDBOOK_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
ULONG state = GetCdromState(DeviceExtension);
|
||
|
||
PAGED_CODE();
|
||
VerifyCalledByThread(DeviceExtension);
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => Checking if audio device changed\n"));
|
||
|
||
if (TEST_FLAG(state, CD_MASK_TEMP)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => delaying -- temp state\n"));
|
||
return;
|
||
}
|
||
|
||
if (DeviceExtension->Stream.UpdateMixerPin == 0) {
|
||
return;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => Audio Device may have changed\n"));
|
||
|
||
if (TEST_FLAG(state, CD_PLAYING)) {
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => playing, so stopping\n"));
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPING);
|
||
return;
|
||
}
|
||
|
||
if (TEST_FLAG(state, CD_STOPPED)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => stopped, updating\n"));
|
||
|
||
} else if (TEST_FLAG(state, CD_PAUSED)) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => paused, updating\n"));
|
||
|
||
//
|
||
// ISSUE-2000/5/24-henrygab - may not need to stop
|
||
// unless mixer becomes -1,
|
||
// since we could then send
|
||
// to the new audio device.
|
||
//
|
||
state = SetCdromState(DeviceExtension, state, CD_STOPPED);
|
||
|
||
}
|
||
|
||
ASSERT(TEST_FLAG(GetCdromState(DeviceExtension), CD_STOPPED));
|
||
|
||
//
|
||
// set the value to zero (iff the value was one)
|
||
// check if the value was one, and if so, update the mixerpin
|
||
//
|
||
|
||
if (InterlockedCompareExchange(&DeviceExtension->Stream.UpdateMixerPin,
|
||
0, 1) == 1) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => Updating MixerPin\n"));
|
||
|
||
//
|
||
// free any in-use play resources
|
||
//
|
||
|
||
RedBookDeallocatePlayResources(DeviceExtension);
|
||
|
||
if (DeviceExtension->Stream.MixerPinId != -1) {
|
||
UninitializeVirtualSource(DeviceExtension);
|
||
}
|
||
|
||
InitializeVirtualSource(DeviceExtension);
|
||
|
||
if (DeviceExtension->Stream.MixerPinId == -1) {
|
||
|
||
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugSysaudio, "[redbook] "
|
||
"STCheckForRemoval => Update of mixerpin "
|
||
"failed -- will retry later\n"));
|
||
InterlockedExchange(&DeviceExtension->Stream.UpdateMixerPin, 1);
|
||
return;
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|