/*++ 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 // for SetKnownGoodDrive() #include // vsprintf() #ifdef _USE_ETW #include "ioctl.tmh" #endif // _USE_ETW #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 (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SUB_Q_DATA_FORMAT)){ Irp->IoStatus.Information = sizeof(CDROM_SUB_Q_DATA_FORMAT); status = STATUS_INFO_LENGTH_MISMATCH; 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] = {0}; UCHAR timeRelative[3] = {0}; 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] = {0}; 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; }