summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/vdd/samples/mscdex/vdd/vcdex.c')
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/vcdex.c1474
1 files changed, 1474 insertions, 0 deletions
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c
new file mode 100644
index 000000000..0d7895855
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c
@@ -0,0 +1,1474 @@
+/*++
+
+Copyright (c) 1991, Microsoft Corporation
+
+Module Name:
+
+ vcdex.c
+
+Abstract:
+
+ Virtual Device Driver for MSCDEX
+
+Environment:
+
+ NT-MVDM (User Mode VDD)
+
+Author:
+
+ Neil Sandlin (neilsa), 3/20/93
+
+Notes:
+
+ Implementation Restrictions-
+
+ Currently, the starting and ending locations returned by the mscdex
+ "audio status info" are not returned by NT drivers. This makes it
+ difficult to maintain these values when the calling applications
+ exit, or when multiple applications are controlling a single drive.
+
+ Currently, this implementation does not validate the length argument of
+ the IOCTL calls. This needs to be added for robustness, but will not
+ affect well-behaved apps.
+
+
+Revision History:
+
+
+
+--*/
+
+//
+// Include files.
+//
+
+#include "windows.h"
+#include "winerror.h"
+#include <vddsvc.h>
+#include <mscdex.h>
+#include "devioctl.h"
+#include "ntddcdrm.h"
+#include "ntdddisk.h"
+#include "vcdex.h"
+
+//
+// Global variables.
+//
+
+PFNSVC apfnSVC [] = {
+ ApiGetNumberOfCDROMDrives,
+ ApiGetCDROMDriveList,
+ ApiGetCopyrightFileName,
+ ApiGetAbstractFileName,
+ ApiGetBDFileName,
+ ApiReadVTOC,
+ ApiReserved,
+ ApiReserved,
+ ApiAbsoluteDiskRead,
+ ApiAbsoluteDiskWrite,
+ ApiReserved,
+ ApiCDROMDriveCheck,
+ ApiMSCDEXVersion,
+ ApiGetCDROMDriveLetters,
+ ApiGetSetVolDescPreference,
+ ApiGetDirectoryEntry,
+ ApiSendDeviceRequest
+};
+
+PDRIVE_INFO DrivePointers[MAXDRIVES];
+PDRIVE_INFO DrvInfo;
+LPREQUESTHEADER VdmReq; // for "send device request"
+USHORT NumDrives = 0, FirstDrive = 0xff;
+DWORD DeviceHeader; // for "get CDROM drive list"
+BYTE LastRealStatus = AUDIO_STATUS_NO_STATUS;
+
+#define IS_DRIVE_CDROM(drive) \
+ (drive < MAXDRIVES && DrivePointers[drive] != NULL)
+
+
+HANDLE hVdd;
+HANDLE hProcessHeap;
+
+
+
+VOID UserBlockHook(VOID);
+VOID UserTerminateHook(USHORT);
+
+
+
+BOOL
+VDDInitialize(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved
+ )
+
+/*++
+
+Routine Description:
+
+ The entry point for the Vdd which handles intialization and termination.
+
+Arguments:
+
+ hVdd - The handle to the VDD
+
+ Reason - flag word thatindicates why Dll Entry Point was invoked
+
+ lpReserved - Unused
+
+Return Value:
+ BOOL bRet - if (dwReason == DLL_PROCESS_ATTACH)
+ TRUE - Dll Intialization successful
+ FALSE - Dll Intialization failed
+ else
+ always returns TRUE
+--*/
+
+{
+ int i;
+
+ switch ( dwReason ) {
+
+ case DLL_PROCESS_ATTACH:
+ hVdd = hDll;
+ hProcessHeap = GetProcessHeap();
+
+ DisableThreadLibraryCalls(hDll);
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Process Attach\n");
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ for (i=0; i<MAXDRIVES; i++)
+
+ if (DrivePointers[i] != NULL) {
+
+ if (DrivePointers[i]->Handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(DrivePointers[i]->Handle);
+ }
+ HeapFree(hProcessHeap, 0, DrivePointers[i]);
+
+ }
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Process Detach\n");
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ return TRUE;
+
+}
+
+
+
+VOID
+VDDRegisterInit(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when the MSCDEXNT TSR makes its RegisterModule
+ call. Most of the initialization is done here instead of in the
+ VDDInitialize routine to improve performance in the case where the
+ VDD is loaded, but not used.
+
+ The main point of this routine is to search for CDROM drives and set
+ up an array of pointers to point to DRIVE_INFO structures. The array
+ is a fixed size array, one for each possible DOS drive. The structures
+ are allocated only if a CDROM drive exists at the corresponding drive
+ letter in the array.
+
+ By doing a CreateFile() to the drive letter of the drive, a handle to
+ the SCSI CDROM class driver is returned. This handle is used for all
+ communication with the drive.
+
+
+
+Return Value:
+
+ SUCCESS - Client carry is clear
+ Client CX = # of CDROM drives
+
+ FAILURE - Client carry is set
+
+--*/
+
+
+{
+ CHAR chRoot [] = "?:\\";
+ USHORT i;
+ HANDLE hDriver;
+ PDRIVE_INFO drvp;
+ static BOOLEAN Initialized = FALSE;
+
+ if (Initialized) {
+ setCF(0);
+ return;
+ }
+
+
+ // Make far pointer with offset zero (DX is para aligned)
+ DeviceHeader = (DWORD) ((getCX() << 16) + (getDX() << 12));
+
+ for (i=0; i<MAXDRIVES; i++) {
+
+ chRoot[0] = i + 'A';
+
+ if (GetDriveType((LPSTR) chRoot) == DRIVE_CDROM) {
+
+ hDriver = OpenPhysicalDrive(i);
+
+ if (hDriver != INVALID_HANDLE_VALUE) {
+
+ drvp = (PDRIVE_INFO)HeapAlloc(hProcessHeap,
+ 0,
+ sizeof(DRIVE_INFO)
+ );
+
+ if(drvp == NULL) {
+ DebugPrint (DEBUG_MOD, "VCDEX: Out of memory on initializetion\n");
+ Initialized = FALSE;
+ setCF(1);
+ return;
+ }
+
+ DrivePointers[i] = drvp;
+ drvp->Handle = hDriver;
+ drvp->DriveNum = i;
+ drvp->ValidVTOC = FALSE;
+ drvp->MediaStatus = MEDCHNG_CHANGED;
+
+ drvp->PlayStart.dw = 0;
+ drvp->PlayCount = 0;
+ GetAudioStatus (drvp);
+
+ NumDrives++;
+ if (FirstDrive == 0xff)
+ FirstDrive = i;
+
+
+ //
+ // Keep the handle close until app really wants to use it
+ //
+ drvp->Handle = INVALID_HANDLE_VALUE;
+ CloseHandle(hDriver);
+
+
+ } else {
+ DrivePointers[i] = NULL;
+ }
+
+ }
+
+ }
+
+ if (NumDrives == 0) {
+
+ setCF(1);
+
+ } else {
+ PDEVICE_HEADER pDev = (PDEVICE_HEADER) GetVDMPointer(
+ ((ULONG)getCX()<<16)+getDX(),
+ 1, FALSE);
+
+ // Put the first valid cdrom drive number in the device header
+ pDev->drive = FirstDrive+1;
+
+ VDDInstallUserHook(hVdd, NULL, UserTerminateHook, UserBlockHook, NULL);
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Initialized\n");
+ Initialized = TRUE;
+
+ setCF(0);
+
+ }
+
+ return;
+}
+
+
+VOID UserTerminateHook(USHORT Pdb)
+{
+ UserBlockHook();
+}
+
+
+VOID UserBlockHook(VOID)
+{
+
+ int DrvNum;
+
+ DrvNum = MAXDRIVES;
+ while (DrvNum--) {
+ if (DrivePointers[DrvNum] &&
+ DrivePointers[DrvNum]->Handle != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle(DrivePointers[DrvNum]->Handle);
+ DrivePointers[DrvNum]->Handle = INVALID_HANDLE_VALUE;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+VOID
+VDDDispatch(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This is the main MSCDEX api function dispatcher. When this routine
+ is entered, an int2f has just been executed. Client registers are
+ set to what they were at the time of the call with the exception
+ of AX, which must contain a handle for the DispatchCall(). The
+ value of AX was pushed on the stack. So, this routine restores it,
+ and uses AL to index into the function call table apfnSVC[].
+
+
+Return Value:
+
+ SUCCESS - Client carry is clear
+ FAILURE - Client carry is set
+
+--*/
+
+{
+
+ LPWORD VdmWordPtr;
+ WORD VdmAX;
+ ULONG VdmAddress;
+
+ //
+ // The TSR pushes AX on the stack. Pick up the value here
+ //
+
+ VdmAddress = ( getSS() << 16 ) | getSP();
+
+ VdmWordPtr = (LPWORD) GetVDMPointer ( VdmAddress, 2, FALSE);
+
+ VdmAX = *VdmWordPtr;
+
+ //
+ // AL has the MSCDEX function code
+ //
+ setAX (VdmAX); //restore AX
+
+ (apfnSVC [VdmAX & 0xFF])();
+
+ return;
+}
+
+
+
+/****************************************************************************
+
+ MSCDEX API SUBROUTINES
+
+ The following routines perform the individual functions specified by
+ the MSCDEX extensions.
+
+
+ ****************************************************************************/
+VOID
+ApiReserved(
+ VOID
+ )
+
+{
+
+ DebugFmt (DEBUG_API, "VCDEX: Reserved Function call, ax=%.4X\n", getAX());
+
+}
+
+
+VOID
+ApiGetNumberOfCDROMDrives(
+ VOID
+ )
+
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Get # of CDROM drives\n");
+
+ setBX (NumDrives);
+
+ if (NumDrives)
+ setCX (FirstDrive);
+
+}
+
+
+VOID
+ApiGetCDROMDriveList(
+ VOID
+ )
+
+{
+
+ PDRIVE_DEVICE_LIST devlist, devlist0;
+ ULONG VdmAddress;
+ USHORT Drive;
+ BYTE Unit;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get CDROM drive list\n");
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ devlist = devlist0 = (PDRIVE_DEVICE_LIST) GetVDMPointer (VdmAddress,
+ MAXDRIVES*sizeof(DRIVE_DEVICE_LIST),
+ FALSE);
+
+ for (Drive=0, Unit=0; Drive<MAXDRIVES; Drive++)
+ if (DrivePointers[Drive] != NULL) {
+ devlist->Unit = Unit;
+ devlist->DeviceHeader = DeviceHeader;
+ devlist++;
+ Unit++;
+ }
+
+ FreeVDMPointer (VdmAddress,
+ MAXDRIVES*sizeof(DRIVE_DEVICE_LIST),
+ devlist0,
+ FALSE);
+
+
+}
+
+VOID
+ApiGetCopyrightFileName(
+ VOID
+ )
+{
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Copyright File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+VOID
+ApiGetAbstractFileName(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Abstract File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+
+VOID
+ApiGetBDFileName(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Bibliographic Doc File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+VOID
+ApiReadVTOC(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Read VTOC\n");
+ setCF(1); // currently not implemented
+
+}
+
+
+
+VOID
+ApiAbsoluteDiskRead(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Absolute Disk Read\n");
+ setCF(1); // currently not implemented
+
+}
+
+VOID
+ApiAbsoluteDiskWrite(
+ VOID
+ )
+{
+ DebugPrint (DEBUG_API, "VCDEX: Absolute Disk Write\n");
+ setCF(1); // read only
+}
+
+
+VOID
+ApiCDROMDriveCheck(
+ VOID
+ )
+
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: CDROM drive check\n");
+
+ setBX (0xADAD); // MSCDEX Signature
+
+ if (IS_DRIVE_CDROM(getCX())) // is CD ROM drive
+ setAX (1); // yes
+ else
+ setAX (0); // no
+
+}
+
+VOID
+ApiMSCDEXVersion(
+ VOID
+ )
+
+{
+ DebugPrint (DEBUG_API, "VCDEX: MSCDEX Version\n");
+ setBX (MSCDEX_VERSION); // MSCDEX Version #
+
+}
+
+VOID
+ApiGetCDROMDriveLetters(
+ VOID
+ )
+
+{
+ ULONG VdmAddress;
+ LPBYTE VdmPtr, VdmPtr0;
+ USHORT Drive;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get CDROM Drive Letters\n");
+
+ VdmAddress = (getES() << 16) | getBX();
+ VdmPtr = VdmPtr0 = (LPBYTE) GetVDMPointer (VdmAddress, MAXDRIVES, FALSE);
+
+ for (Drive=0; Drive<MAXDRIVES; Drive++)
+ if (DrivePointers[Drive] != NULL)
+ *VdmPtr++ = (BYTE) Drive;
+
+ FreeVDMPointer (VdmAddress, MAXDRIVES, VdmPtr0, FALSE);
+
+}
+
+
+VOID
+ApiGetSetVolDescPreference(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Set Volume Descriptor Preference\n");
+ setCF(1); // currently not implemented
+
+}
+
+VOID
+ApiGetDirectoryEntry(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Directory Entry\n");
+ setCF(1); // currently not implemented
+
+}
+
+
+
+VOID
+ApiSendDeviceRequest(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ BOOL Success;
+ DWORD BytesReturned;
+ DWORD absStart, absEnd;
+ int DrvNum;
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ VdmReq = (LPREQUESTHEADER) GetVDMPointer (VdmAddress,
+ sizeof (REQUESTHEADER),
+ FALSE);
+
+
+ DebugFmt (DEBUG_IO, ">RQ %d ", (DWORD) VdmReq->rhFunction);
+
+ DrvNum = getCX();
+
+ if (!IS_DRIVE_CDROM(DrvNum)) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDSTAT_DONE | CDERR_UNKNOWN_UNIT;
+ return;
+
+ }
+
+ DrvInfo = DrivePointers[DrvNum];
+
+ if (DrvInfo->Handle == INVALID_HANDLE_VALUE) {
+ DrvInfo->Handle = OpenPhysicalDrive(DrvInfo->DriveNum);
+ if (DrvInfo->Handle == INVALID_HANDLE_VALUE) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDSTAT_DONE | CDERR_UNKNOWN_UNIT;
+ HeapFree(hProcessHeap, 0, DrvInfo);
+ DrivePointers[DrvNum] = NULL;
+ NumDrives--;
+ if (FirstDrive == DrvNum) {
+ FirstDrive = 0xff;
+ while (++DrvNum < MAXDRIVES) {
+ if (DrivePointers[DrvNum]) {
+ FirstDrive = DrvNum;
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+
+
+ GetAudioStatus (DrvInfo);
+
+ if (DrvInfo->Playing)
+ VdmReq->rhStatus |= CDSTAT_BUSY;
+
+ switch (VdmReq->rhFunction) {
+
+ case IOCTL_READ:
+
+ IOCTLRead();
+
+ break;
+
+ case IOCTL_WRITE:
+
+ IOCTLWrite();
+
+ break;
+
+ case INPUT_FLUSH:
+ case OUTPUT_FLUSH:
+ case DEVICE_OPEN:
+ case DEVICE_CLOSE:
+ case READ_LONG:
+ case READ_LONG_PREFETCH:
+ case SEEK:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX Device Request\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ case PLAY_AUDIO: {
+
+ CDROM_PLAY_AUDIO_MSF PlayAudioMSF;
+ PPLAY_AUDIO_BLOCK playreq = (PPLAY_AUDIO_BLOCK) VdmReq;
+
+ if (playreq->addrmode == MODE_HSG) {
+
+ absStart = playreq->startsect.dw;
+ PlayAudioMSF.StartingM = (BYTE) (absStart / (75 * 60));
+ PlayAudioMSF.StartingS = (BYTE) ((absStart / 75) % 60);
+ PlayAudioMSF.StartingF = (BYTE) (absStart % 75);
+
+ } else if (playreq->addrmode == MODE_REDBOOK) {
+
+ PlayAudioMSF.StartingM = playreq->startsect.b[2];
+ PlayAudioMSF.StartingS = playreq->startsect.b[1];
+ PlayAudioMSF.StartingF = playreq->startsect.b[0];
+
+ absStart = (PlayAudioMSF.StartingM * 75 * 60) +
+ (PlayAudioMSF.StartingS * 75) +
+ (PlayAudioMSF.StartingF);
+ } else {
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_PARAMETER;
+ break;
+
+ }
+
+ absEnd = absStart + playreq->numsect - 1;
+
+ PlayAudioMSF.EndingM = (BYTE) (absEnd / (75 * 60));
+ PlayAudioMSF.EndingS = (BYTE) ((absEnd / 75) % 60);
+ PlayAudioMSF.EndingF = (BYTE) (absEnd % 75);
+
+ DebugPrint (DEBUG_IO, "Play ");
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_PLAY_AUDIO_MSF,
+ (LPVOID) &PlayAudioMSF,
+ sizeof (CDROM_PLAY_AUDIO_MSF),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, PLAY_AUDIO,0);
+
+ else {
+
+ DrvInfo->Playing = TRUE;
+ DrvInfo->Paused = FALSE;
+ DrvInfo->PlayStart.dw = playreq->startsect.dw;
+ DrvInfo->PlayCount = playreq->numsect;
+
+ }
+
+ break;
+ }
+
+ case STOP_AUDIO:
+
+ if (DrvInfo->Playing) {
+
+ DebugPrint (DEBUG_IO, "Pause ");
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_PAUSE_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, STOP_AUDIO,0);
+
+ else {
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = TRUE;
+ }
+
+ } else {
+
+ DebugPrint (DEBUG_IO, "Stop ");
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_STOP_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ // Fake out GetAudioStatus to simulate stop
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = FALSE;
+ LastRealStatus = AUDIO_STATUS_PLAY_COMPLETE;
+
+ if (!Success) {
+ DWORD dwErr;
+
+ dwErr = GetLastError();
+ if (dwErr == ERROR_MR_MID_NOT_FOUND ||
+ dwErr == ERROR_NO_MEDIA_IN_DRIVE )
+ {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ }
+
+ break;
+
+ case WRITE_LONG:
+ case WRITE_LONG_VERIFY:
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_WRITE_PROTECT;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ break;
+
+ case RESUME_AUDIO:
+
+ if (DrvInfo->Paused) {
+
+ DebugPrint (DEBUG_IO, "Resume ");
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_RESUME_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, RESUME_AUDIO,0);
+
+ } else {
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_GENERAL;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX Device Request\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+
+ VdmReq->rhStatus |= CDSTAT_DONE;
+
+ DebugFmt (DEBUG_IO, ": %.4X ", VdmReq->rhStatus);
+
+}
+
+VOID
+IOCTLRead(
+ VOID
+ )
+
+{
+
+ LPBYTE Buffer;
+ BOOL Success;
+ DWORD BytesReturned;
+
+ Buffer = GetVDMPointer ((ULONG)VdmReq->irwrBuffer, 16, FALSE);
+
+ DebugFmt (DEBUG_IO, "iord %d ", (DWORD) *Buffer);
+
+ switch (*Buffer) {
+
+ case IOCTLR_AUDINFO: {
+
+ PIOCTLR_AUDINFO_BLOCK audinfo = (PIOCTLR_AUDINFO_BLOCK) Buffer;
+ VOLUME_CONTROL VolumeControl;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_GET_VOLUME,
+ (LPVOID) NULL, 0,
+ (LPVOID) &VolumeControl,
+ sizeof (VOLUME_CONTROL),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (Success) {
+
+ // no support for input=>output channel manipulation
+ audinfo->chan0 = 0;
+ audinfo->chan1 = 1;
+ audinfo->chan2 = 2;
+ audinfo->chan3 = 3;
+
+ audinfo->vol0 = VolumeControl.PortVolume[0];
+ audinfo->vol1 = VolumeControl.PortVolume[1];
+ audinfo->vol2 = VolumeControl.PortVolume[2];
+ audinfo->vol3 = VolumeControl.PortVolume[3];
+
+ } else {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+ }
+
+ case IOCTLR_DEVSTAT: {
+
+ PIOCTLR_DEVSTAT_BLOCK devstat = (PIOCTLR_DEVSTAT_BLOCK) Buffer;
+
+ devstat->devparms = DEVSTAT_DOOR_UNLOCKED |
+ DEVSTAT_SUPPORTS_RBOOK;
+
+
+ if (!DrvInfo->StatusAvailable) {
+
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ switch (DrvInfo->LastError) {
+ case ERROR_NO_MEDIA_IN_DRIVE:
+ devstat->devparms |= DEVSTAT_NO_DISC |
+ DEVSTAT_DOOR_OPEN;
+
+ DebugFmt (DEBUG_STATUS, ":%.4X ", devstat->devparms);
+
+ break;
+ //BUGBUG case for recently inserted (80000016) - see below
+ }
+
+ break;
+ }
+
+ if (!(DrvInfo->current.Control & AUDIO_DATA_TRACK))
+ devstat->devparms |= DEVSTAT_PLAYS_AV;
+
+ break;
+ }
+
+ case IOCTLR_VOLSIZE: {
+
+ PIOCTLR_VOLSIZE_BLOCK volsize = (PIOCTLR_VOLSIZE_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+
+ Track = &cdromtoc->TrackData[cdromtoc->LastTrack];
+
+ volsize->size = (DWORD) ( (Track->Address[1]*60*75) +
+ (Track->Address[2]*75) +
+ Track->Address[3] );
+
+ }
+ break;
+ }
+
+ case IOCTLR_MEDCHNG: {
+
+ PIOCTLR_MEDCHNG_BLOCK medptr = (PIOCTLR_MEDCHNG_BLOCK) Buffer;
+ BYTE status = DrvInfo->MediaStatus;
+
+ if (status == MEDCHNG_NOT_CHANGED) {
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_CHECK_VERIFY,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (Success)
+
+ medptr->medbyte = MEDCHNG_NOT_CHANGED;
+
+ else {
+
+ medptr->medbyte = MEDCHNG_CHANGED;
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ } else
+ medptr->medbyte = DrvInfo->MediaStatus;
+
+ break;
+ }
+
+ case IOCTLR_DISKINFO: {
+
+ PIOCTLR_DISKINFO_BLOCK diskinfo = (PIOCTLR_DISKINFO_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+ diskinfo->tracklow = cdromtoc->FirstTrack;
+ diskinfo->trackhigh = cdromtoc->LastTrack;
+
+ Track = &cdromtoc->TrackData[cdromtoc->LastTrack];
+
+ diskinfo->startleadout.b[0] = Track->Address[3];
+ diskinfo->startleadout.b[1] = Track->Address[2];
+ diskinfo->startleadout.b[2] = Track->Address[1];
+ diskinfo->startleadout.b[3] = Track->Address[0];
+
+ } else {
+
+ // zeroes apparently needed when not there physically
+ diskinfo->tracklow = 0;
+ diskinfo->trackhigh = 0;
+ diskinfo->startleadout.dw = 0;
+
+ }
+ break;
+ }
+
+ case IOCTLR_TNOINFO: {
+
+ PIOCTLR_TNOINFO_BLOCK tnoinfo = (PIOCTLR_TNOINFO_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+
+ if (tnoinfo->trknum > cdromtoc->LastTrack) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_SECT_NOTFOUND;
+ break;
+ }
+
+ Track = &cdromtoc->TrackData[tnoinfo->trknum-1];
+ tnoinfo->start.b[0] = Track->Address[3];
+ tnoinfo->start.b[1] = Track->Address[2];
+ tnoinfo->start.b[2] = Track->Address[1];
+ tnoinfo->start.b[3] = Track->Address[0];
+
+ tnoinfo->trkctl = Track->Control;
+ }
+
+ break;
+ }
+
+ case IOCTLR_QINFO: {
+
+ PIOCTLR_QINFO_BLOCK qinfo = (PIOCTLR_QINFO_BLOCK) Buffer;
+
+ if (DrvInfo->StatusAvailable) {
+
+ qinfo->ctladr = DrvInfo->current.Control | DrvInfo->current.ADR<<4;
+ qinfo->trknum = DrvInfo->current.TrackNumber;
+ qinfo->pointx = DrvInfo->current.IndexNumber;
+ qinfo->min = DrvInfo->current.TrackRelativeAddress[1];
+ qinfo->sec = DrvInfo->current.TrackRelativeAddress[2];
+ qinfo->frame = DrvInfo->current.TrackRelativeAddress[3];
+
+ qinfo->zero = DrvInfo->current.AbsoluteAddress[0];
+ qinfo->apmin = DrvInfo->current.AbsoluteAddress[1];
+ qinfo->apsec = DrvInfo->current.AbsoluteAddress[2];
+ qinfo->apframe = DrvInfo->current.AbsoluteAddress[3];
+
+ } else {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+ }
+
+ case IOCTLR_UPCCODE: {
+
+ PIOCTLR_UPCCODE_BLOCK upccode = (PIOCTLR_UPCCODE_BLOCK) Buffer;
+ SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
+ static CDROM_SUB_Q_DATA_FORMAT subqfmt = {IOCTL_CDROM_MEDIA_CATALOG};
+ int i;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_Q_CHANNEL,
+ (LPVOID) &subqfmt,
+ sizeof (CDROM_SUB_Q_DATA_FORMAT),
+ (LPVOID) &MediaCatalog,
+ sizeof (SUB_Q_MEDIA_CATALOG_NUMBER),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, IOCTL_READ, IOCTLR_UPCCODE);
+
+ else {
+
+ if (MediaCatalog.Mcval) {
+
+ // The author is uncertain that this is the correct method,
+ // but it appears to work empirically.
+ for (i=0; i<7; i++)
+ upccode->upcean[i] = MediaCatalog.MediaCatalog[i];
+
+ } else
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_SECT_NOTFOUND;
+
+ }
+
+ break;
+ }
+
+ case IOCTLR_AUDSTAT: {
+ PIOCTLR_AUDSTAT_BLOCK audstat = (PIOCTLR_AUDSTAT_BLOCK) Buffer;
+
+ audstat->audstatbits = 0;
+
+ if (DrvInfo->Paused)
+ audstat->audstatbits |= AUDSTAT_PAUSED;
+
+ audstat->startloc.dw = DrvInfo->PlayStart.dw;
+ audstat->endloc.dw = DrvInfo->PlayCount;
+
+ break;
+ }
+
+
+ case IOCTLR_RADDR:
+ case IOCTLR_LOCHEAD:
+ case IOCTLR_ERRSTAT:
+ case IOCTLR_DRVBYTES:
+ case IOCTLR_SECTSIZE:
+ case IOCTLR_SUBCHANINFO:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX IOCTL Read\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX IOCTL Read\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+}
+
+VOID
+IOCTLWrite(
+ VOID
+ )
+
+{
+ LPBYTE Buffer;
+ BOOL Success;
+ DWORD BytesReturned;
+
+ Buffer = GetVDMPointer ((ULONG)VdmReq->irwrBuffer, 16, FALSE);
+
+ DebugFmt (DEBUG_IO, "iowt %d ", (DWORD) *Buffer);
+
+ switch (*Buffer) {
+
+ case IOCTLW_EJECT:
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_EJECT_MEDIA,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_EJECT);
+ break;
+
+ case IOCTLW_LOCKDOOR: {
+
+ PREVENT_MEDIA_REMOVAL MediaRemoval;
+ PIOCTLW_LOCKDOOR_BLOCK lockdoor = (PIOCTLW_LOCKDOOR_BLOCK) Buffer;
+
+ MediaRemoval.PreventMediaRemoval = (BOOLEAN) lockdoor->lockfunc;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_MEDIA_REMOVAL,
+ (LPVOID) &MediaRemoval,
+ sizeof(PREVENT_MEDIA_REMOVAL),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_LOCKDOOR);
+ break;
+ }
+
+ case IOCTLW_AUDINFO: {
+ PIOCTLW_AUDINFO_BLOCK audinfo = (PIOCTLW_AUDINFO_BLOCK) Buffer;
+ VOLUME_CONTROL VolumeControl;
+
+ // note: no support for input=>output channel manipulation
+ VolumeControl.PortVolume[0] = audinfo->vol0;
+ VolumeControl.PortVolume[1] = audinfo->vol1;
+ VolumeControl.PortVolume[2] = audinfo->vol2;
+ VolumeControl.PortVolume[3] = audinfo->vol3;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_SET_VOLUME,
+ (LPVOID) &VolumeControl,
+ sizeof (VOLUME_CONTROL),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_AUDINFO);
+ break;
+ }
+
+
+
+ case IOCTLW_RESETDRV:
+ case IOCTLW_DRVBYTES:
+ case IOCTLW_CLOSETRAY:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX IOCTL Write\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX IOCTL Write\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+
+
+}
+
+
+/**************************************************************************
+
+ INTERNAL UTILITY ROUTINES
+
+ **************************************************************************/
+
+PCDROM_TOC
+ReadTOC (
+ PDRIVE_INFO DrvInfo
+ )
+/*++
+
+Routine Description:
+
+ Because several MSCDEX functions return information that is in the
+ Volume Table Of Contents (VTOC), this routine is called to cache
+ the TOC in the DRIVE_INFO structure. Subsequent operations that
+ request information from the VTOC will not have to get it from
+ the drive.
+
+Return Value:
+
+ DWORD value from GetLastError()
+
+--*/
+
+{
+ BOOL Success = TRUE;
+ DWORD BytesReturned;
+
+ if ((DrvInfo->ValidVTOC) &&
+ (DrvInfo->MediaStatus == MEDCHNG_NOT_CHANGED))
+ return(&DrvInfo->VTOC);
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_TOC,
+ (LPVOID) NULL, 0,
+ (LPVOID) &DrvInfo->VTOC, sizeof (CDROM_TOC),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success) {
+ DrvInfo->ValidVTOC = FALSE;
+ ProcessError (DrvInfo, 0, 0);
+ return (NULL);
+ }
+
+ DrvInfo->ValidVTOC = TRUE;
+ DrvInfo->MediaStatus = MEDCHNG_NOT_CHANGED;
+ return (&DrvInfo->VTOC);
+
+
+}
+
+BOOLEAN
+GetAudioStatus(
+ PDRIVE_INFO DrvInfo
+ )
+
+/*++
+
+ Because the AudioStatus byte does not statically reflect the difference
+ between paused and stopped, we have to try to watch for the transition
+ from one state to another to keep track of it.
+
+--*/
+{
+
+ static CDROM_SUB_Q_DATA_FORMAT subqfmt = {IOCTL_CDROM_CURRENT_POSITION};
+ DWORD BytesReturned;
+ BYTE AudStat;
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = FALSE;
+
+ DrvInfo->StatusAvailable = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_Q_CHANNEL,
+ (LPVOID) &subqfmt,
+ sizeof (CDROM_SUB_Q_DATA_FORMAT),
+ (LPVOID) &DrvInfo->current,
+ sizeof (SUB_Q_CURRENT_POSITION),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (DrvInfo->StatusAvailable) {
+
+ AudStat = DrvInfo->current.Header.AudioStatus;
+
+ DebugFmt (DEBUG_STATUS, "+%.2X ", AudStat);
+
+ switch (AudStat) {
+
+ case AUDIO_STATUS_IN_PROGRESS:
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = TRUE;
+ LastRealStatus = AudStat;
+ break;
+
+ case AUDIO_STATUS_PAUSED:
+
+ if (LastRealStatus == AUDIO_STATUS_IN_PROGRESS) {
+
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = TRUE;
+
+ }
+ break;
+
+ case AUDIO_STATUS_PLAY_ERROR:
+ case AUDIO_STATUS_PLAY_COMPLETE:
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = FALSE;
+ LastRealStatus = AudStat;
+ break;
+
+ }
+
+ } else {
+ DrvInfo->LastError = GetLastError();
+ }
+
+ return (DrvInfo->StatusAvailable);
+
+}
+
+
+DWORD
+ProcessError(
+ PDRIVE_INFO DrvInfo,
+ USHORT Command,
+ USHORT Subcmd
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a DeviceIoControl() fails. The extended
+ error code is retrieved, and status bits are set according to the
+ operation that was in progress.
+
+ The DriveInfo Handle is closed
+
+
+ BUGBUG: At the time of this writing, NT fails to remap status 80000016,
+ (when a disc is re-inserted into the drive). So the case for MID_NOT_FOUND
+ was added below. This should be changed to the appropriate code when
+ this error status is properly mapped.
+
+
+Return Value:
+
+ DWORD value from GetLastError()
+
+--*/
+
+
+{
+ DWORD err;
+
+ err = GetLastError();
+
+ DebugFmt (DEBUG_ERROR, "Err! %d, ", Command);
+ DebugFmt (DEBUG_ERROR, "%d: ", Subcmd);
+ DebugFmt (DEBUG_ERROR, "%.8X\n", err);
+
+ switch (err) {
+
+ case ERROR_MR_MID_NOT_FOUND: // To cover unmapped 80000016
+ case ERROR_NO_MEDIA_IN_DRIVE:
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_NOT_READY;
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ break;
+
+ default:
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_GENERAL;
+
+ }
+
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ return (err);
+
+}
+
+
+
+HANDLE
+OpenPhysicalDrive(
+ int DriveNum
+ )
+/*++
+
+Routine Description:
+
+ int DriveNum; Zero based (0 = A, 1 = B, 2 = C ...)
+
+Return Value:
+
+ HANDLE Drive Handle as returned from CreateFile
+
+--*/
+{
+ HANDLE hDrive;
+ CHAR chDrive [] = "\\\\.\\?:";
+
+ chDrive[4] = DriveNum + 'A';
+
+ hDrive = CreateFile (chDrive,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ 0,
+ (HANDLE) NULL);
+
+
+ return hDrive;
+}