|
|
/******************************Module*Header*******************************\
* Module Name: cdapi.c * * This module encapsulates the CD-ROM device into a set of callable apis. * The api's are implemented using the scsi cdrom IOCTLS. * * Created: 02-11-93 * Author: Stephen Estrop [StephenE] * * Copyright (c) 1993 Microsoft Corporation \**************************************************************************/ #ifdef USE_IOCTLS
#pragma warning( once : 4201 4214 )
#define NOOLE
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntdddisk.h>
#include <windows.h> /* required for all Windows applications */
#include <windowsx.h>
#include <string.h>
#include "resource.h"
#include "cdplayer.h"
#include "cdapi.h"
#include "scan.h"
#include "trklst.h"
/* -------------------------------------------------------------------------
** ** High level routines ** ** ------------------------------------------------------------------------- */
/******************************Public*Routine******************************\
* CheckStatus * * Check return code for known bad codes and inform * user how to correct (if possible) the problem. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CheckStatus( LPSTR szCaller, DWORD status, int cdrom ) {
TCHAR s[100];
if (status==ERROR_SUCCESS) return;
switch( status ) {
case ERROR_GEN_FAILURE: wsprintf( s, IdStr( STR_ERR_GEN ), g_Devices[cdrom]->drive, szCaller ); break;
case ERROR_NO_MEDIA_IN_DRIVE: wsprintf( s, IdStr( STR_ERR_NO_MEDIA ), g_Devices[cdrom]->drive ); NoMediaUpdate( cdrom ); break;
case ERROR_UNRECOGNIZED_MEDIA: wsprintf( s, IdStr( STR_ERR_UNREC_MEDIA ), g_Devices[cdrom]->drive ); if (!(g_Devices[cdrom]->State & CD_DATA_CD_LOADED)) NoMediaUpdate( cdrom ); break;
case ERROR_FILE_NOT_FOUND: wsprintf( s, IdStr( STR_ERR_NO_DEVICE ), szCaller, g_Devices[cdrom]->drive ); NoMediaUpdate( cdrom ); break;
case ERROR_INVALID_FUNCTION: wsprintf( s, IdStr( STR_ERR_INV_DEV_REQ ), g_Devices[cdrom]->drive ); break;
case ERROR_NOT_READY: wsprintf( s, IdStr( STR_ERR_NOT_READY ), g_Devices[cdrom]->drive ); NoMediaUpdate( cdrom ); break;
case ERROR_SECTOR_NOT_FOUND: wsprintf( s, IdStr( STR_ERR_BAD_SEC ), g_Devices[cdrom]->drive ); break;
case ERROR_IO_DEVICE: wsprintf( s, IdStr( STR_ERR_IO_ERROR ), g_Devices[cdrom]->drive ); break;
default: wsprintf( s, IdStr( STR_ERR_DEFAULT ), g_Devices[cdrom]->drive, szCaller, status ); break;
}
#if DBG
/* StatusLine( SL_ERROR, s ); */ SetWindowText( g_hwndStatusbar, s ); #endif
}
/******************************Public*Routine******************************\
* CheckUnitCdrom * * Queries the device state, checking to see if a disc has been ejected or * inserted. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CheckUnitCdrom( int cdrom ) { DWORD status;
if ( g_Devices[cdrom]->hCd == NULL ) {
HANDLE hTemp; TCHAR chDevRoot[] = TEXT("\\\\.\\A:");
chDevRoot[4] = g_Devices[cdrom]->drive; hTemp = CreateFile( chDevRoot, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hTemp == INVALID_HANDLE_VALUE ) {
g_Devices[cdrom]->hCd = NULL; return; } else {
g_Devices[cdrom]->hCd = hTemp; } }
status = TestUnitReadyCdrom( g_Devices[cdrom]->hCd );
if (g_Devices[cdrom]->State & CD_NO_CD) {
if (status == ERROR_SUCCESS) {
/*
** A new disc has been inserted, scan it now. */
RescanDevice( g_hwndApp, cdrom ); } } else {
if (status != ERROR_SUCCESS) {
/*
** Disc has been ejected. */
NoMediaUpdate( cdrom ); } } }
/******************************Public*Routine******************************\
* NoMediaUpdate * * Update the user display when it is found that no media is in the device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void NoMediaUpdate( int cdrom ) { BOOL fChangePlayButtons;
if ( cdrom == g_CurrCdrom ) { fChangePlayButtons = TRUE; } else { fChangePlayButtons = FALSE; }
g_Devices[cdrom]->State = (CD_NO_CD | CD_STOPPED);
if (fChangePlayButtons) { SetPlayButtonsEnableState(); }
TimeAdjustInitialize( cdrom ); }
/******************************Public*Routine******************************\
* EjectTheCdromDisc * * Eject the disc from the specified cdrom device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL EjectTheCdromDisc( IN INT cdrom ) { DWORD status;
/*
** Stop the drive first */
status = StopCdrom( g_Devices[cdrom]->hCd );
/*
** Eject the disc */
status = EjectCdrom( g_Devices[cdrom]->hCd );
CheckStatus( "EjectCdrom", status, cdrom );
return status == ERROR_SUCCESS; }
/******************************Public*Routine******************************\
* PlayCurrTrack * * Set cdrom device playing from start MSF to end MSF of current * track. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL PlayCurrTrack( int cdrom ) { DWORD status; CDROM_PLAY_AUDIO_MSF pam; int retry,min,sec,endindex; int i; PTRACK_PLAY tr;
tr = CURRTRACK( cdrom ); if (tr==NULL) {
return( FALSE );
}
sec = TRACK_S(cdrom,tr->TocIndex) + CDTIME(cdrom).TrackCurSec; min = TRACK_M(cdrom,tr->TocIndex) + CDTIME(cdrom).TrackCurMin; min += (sec / 60); sec = (sec % 60); pam.StartingM = min; pam.StartingS = sec; pam.StartingF = TRACK_F(cdrom,tr->TocIndex);
endindex = FindContiguousEnd( cdrom, tr );
pam.EndingM = TRACK_M(cdrom,endindex); pam.EndingS = TRACK_S(cdrom,endindex); pam.EndingF = TRACK_F(cdrom,endindex);
/*
** for some reason, sometimes the lead out track ** gived bad values, because when we try to ** play the last track, we get an error. However, ** if we back up a little bit from what is reported ** to us as the end of the last track, we can get ** it to play. Below is a hack to do just that... */
retry = 0;
do {
status = PlayCdrom( g_Devices[cdrom]->hCd, &pam );
if ( (status != ERROR_SUCCESS) ) {
/*
** Didn't play, so try backing off a little bit ** at the end of the track */
retry++;
i = (INT)pam.EndingF - 30; if (i<0) {
pam.EndingF = (UCHAR)(70 + i);
if (pam.EndingS!=0) {
pam.EndingS--;
} else {
pam.EndingS=59; pam.EndingM--;
}
} else {
pam.EndingF = (UCHAR)i;
}
/*
** Store the information in our structures so that ** we don't have to recompute this next time... */
TRACK_M(cdrom,endindex) = pam.EndingM; TRACK_S(cdrom,endindex) = pam.EndingS; TRACK_F(cdrom,endindex) = pam.EndingF;
} else
retry = 15;
} while ((retry<15) && (status!=ERROR_SUCCESS));
CheckStatus( "PlayCurrTrack", status, cdrom );
if (status == ERROR_SUCCESS) {
ValidatePosition( cdrom );
}
return status == ERROR_SUCCESS;
}
/******************************Public*Routine******************************\
* StopTheCdromDrive * * Tell the cdrom device to stop playing * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL StopTheCdromDrive( int cdrom ) {
DWORD status;
status = StopCdrom( g_Devices[cdrom]->hCd );
CheckStatus( "StopCdrom", status, cdrom );
return status == ERROR_SUCCESS; }
/******************************Public*Routine******************************\
* PauseTheCdromDrive * * Tell the cdrom device to pause playing * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL PauseTheCdromDrive( int cdrom ) {
DWORD status;
status = PauseCdrom( g_Devices[cdrom]->hCd );
CheckStatus( "PauseCdrom", status, cdrom );
return status == ERROR_SUCCESS; }
/******************************Public*Routine******************************\
* ResumeTheCdromDrive * * Tell the cdrom device to resume playing * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL ResumeTheCdromDrive( int cdrom ) {
DWORD status;
status = ResumeCdrom( g_Devices[cdrom]->hCd );
CheckStatus( "ResumeCdrom", status, cdrom ); if ( status == ERROR_NOT_READY ) NoMediaUpdate( cdrom ); else ValidatePosition( cdrom );
return status == ERROR_SUCCESS; }
/******************************Public*Routine******************************\
* SeekToCurrSecond * * Seek to the position on the disc represented by the * current time (position) information in gDevices, and * continue playing to the end of the current track. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL SeekToCurrSecond( int cdrom ) {
DWORD status; CDROM_PLAY_AUDIO_MSF pam; int retry,i,endindex; PTRACK_PLAY tr; SUB_Q_CHANNEL_DATA subq; CDROM_SUB_Q_DATA_FORMAT df;
/*
** Build starting and ending positions for play */
tr = CDTIME(cdrom).CurrTrack; if (tr==NULL) {
return( FALSE ); }
/*
** This routine sometimes wants to play from the current position ** through the end of the contiguous play. Since the current ** position is only being stored accurate down to seconds, we get ** the current position, see if it's reasonably close to our ** starting position, then start the play from the actual current ** position. */
df.Format = IOCTL_CDROM_CURRENT_POSITION; df.Track = (UCHAR)CDTIME(cdrom).CurrTrack->TocIndex; GetCdromSubQData( g_Devices[ cdrom ]->hCd, &subq, &df );
pam.StartingM = (UCHAR)(TRACK_M(cdrom,tr->TocIndex) + CDTIME(cdrom).TrackCurMin); pam.StartingS = (UCHAR)(TRACK_S(cdrom,tr->TocIndex) + CDTIME(cdrom).TrackCurSec); pam.StartingF = 0;
i = pam.StartingM * 60 + pam.StartingS; i-= (INT) subq.CurrentPosition.AbsoluteAddress[1] * 60; i-= (INT) subq.CurrentPosition.AbsoluteAddress[2];
if (ABS(i) <= 1) {
pam.StartingM = (INT) subq.CurrentPosition.AbsoluteAddress[1]; pam.StartingS = (INT) subq.CurrentPosition.AbsoluteAddress[2]; pam.StartingF = (INT) subq.CurrentPosition.AbsoluteAddress[3];
}
if (pam.StartingS > 59) { pam.StartingM++; pam.StartingS = (UCHAR)(pam.StartingS - 60); }
if ((CDTIME(cdrom).TrackCurMin==0) && (CDTIME(cdrom).TrackCurSec==0)) pam.StartingF = TRACK_F(cdrom,tr->TocIndex);
if (g_Devices[ cdrom ]->State & CD_PLAYING) {
endindex = FindContiguousEnd( cdrom, tr );
pam.EndingM = TRACK_M(cdrom,endindex); pam.EndingS = TRACK_S(cdrom,endindex); pam.EndingF = TRACK_F(cdrom,endindex);
} else {
endindex = 0; pam.EndingM = pam.StartingM; pam.EndingS = pam.StartingS; pam.EndingF = pam.StartingF;
}
retry = 0;
do {
status = PlayCdrom( g_Devices[ cdrom ]->hCd, &pam );
if (status != ERROR_SUCCESS) {
/*
** Didn't play, so try backing off a little bit ** at the end of the track */
retry++;
i = (INT)pam.EndingF - 30; if (i<0) {
pam.EndingF = (UCHAR)(70 + i);
if (pam.EndingS!=0) {
pam.EndingS--;
} else {
pam.EndingS=59; pam.EndingM--;
}
} else {
pam.EndingF = (UCHAR)i;
}
/*
** Store the information in our structures so that ** we don't have to recompute this next time... */
TRACK_M(cdrom,endindex) = pam.EndingM; TRACK_S(cdrom,endindex) = pam.EndingS; TRACK_F(cdrom,endindex) = pam.EndingF;
} else
retry = 15;
} while ((retry<15) && (status!=ERROR_SUCCESS));
CheckStatus( "SeekToCurrSec", status, cdrom );
if (status == ERROR_SUCCESS) {
ValidatePosition(cdrom);
}
return status == ERROR_SUCCESS;
}
/******************************Public*Routine******************************\
* GetCurrPos * * Query cdrom device for its current position and status * and return information in callers buffer. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL GetCurrPos( int cdrom, PCURRPOS CpPtr ) {
DWORD status; SUB_Q_CHANNEL_DATA subq; CDROM_SUB_Q_DATA_FORMAT df;
/*
** Tell lower layer what we want it to do...in this case, ** we need to specify which SubQData format we want returned. ** This is exported from scsicdrom.sys to the user layer ** so that it could be implemented in one call, instead of ** four separate calls (there are four SubQData formats) */
/*
** Set up for current position SubQData format. */
df.Format = IOCTL_CDROM_CURRENT_POSITION; if (CDTIME(cdrom).CurrTrack != NULL) {
df.Track = (UCHAR)CDTIME(cdrom).CurrTrack->TocIndex; status = GetCdromSubQData( g_Devices[ cdrom ]->hCd, &subq, &df );
} else {
status = (DWORD)~ERROR_SUCCESS;
}
if (status==ERROR_SUCCESS) {
CpPtr->AudioStatus = subq.CurrentPosition.Header.AudioStatus; CpPtr->Track = (INT)subq.CurrentPosition.TrackNumber; CpPtr->Index = (INT)subq.CurrentPosition.IndexNumber; CpPtr->m = (INT)subq.CurrentPosition.TrackRelativeAddress[1]; CpPtr->s = (INT)subq.CurrentPosition.TrackRelativeAddress[2]; CpPtr->f = (INT)subq.CurrentPosition.TrackRelativeAddress[3]; CpPtr->ab_m = (INT)subq.CurrentPosition.AbsoluteAddress[1]; CpPtr->ab_s = (INT)subq.CurrentPosition.AbsoluteAddress[2]; CpPtr->ab_f = (INT)subq.CurrentPosition.AbsoluteAddress[3];
} else {
CpPtr->AudioStatus = 0; CpPtr->Track = 0; CpPtr->Index = 0; CpPtr->m = 0; CpPtr->s = 0; CpPtr->f = 0; CpPtr->ab_m = 0; CpPtr->ab_s = 0; CpPtr->ab_f = 0;
}
CheckStatus( "GetCurrPos", status, cdrom );
return status==ERROR_SUCCESS; }
/******************************Public*Routine******************************\
* SeekToTrackAndHold * * Seek to specified track and enter hold state. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL SeekToTrackAndHold( int cdrom, int tindex ) {
DWORD status; CDROM_SEEK_AUDIO_MSF sam;
sam.M = TRACK_M(cdrom,tindex); sam.S = TRACK_S(cdrom,tindex); sam.F = TRACK_F(cdrom,tindex);
status = SeekCdrom( g_Devices[ cdrom ]->hCd, &sam );
CheckStatus( "SeekToTrackAndHold", status, cdrom );
if (status == ERROR_SUCCESS) {
ValidatePosition( cdrom );
}
return status == ERROR_SUCCESS; }
/* -------------------------------------------------------------------------
** ** Low level routines ** ** ------------------------------------------------------------------------- */
/******************************Public*Routine******************************\
* GetCdromTOC * * This routine will get the table of contents from * a CDRom device. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD GetCdromTOC( HANDLE DeviceHandle, PCDROM_TOC TocPtr )
{ DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_READ_TOC, NULL, 0, (LPVOID)TocPtr, sizeof(CDROM_TOC), &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); }
}
/******************************Public*Routine******************************\
* StopCdrom * * This routine will stop a CDRom device that is playing. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD StopCdrom( HANDLE DeviceHandle ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* PauseCdrom * * This routine will pause a CDRom device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD PauseCdrom( HANDLE DeviceHandle ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* ResumeCdrom * * This routine will resume a paused CDRom device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD ResumeCdrom( HANDLE DeviceHandle )
{ DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* PlayCdrom * * This routine plays a CDRom device starting and ending at the MSF * positions specified in the structure passed in. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD PlayCdrom( HANDLE DeviceHandle, PCDROM_PLAY_AUDIO_MSF PlayAudioPtr ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_PLAY_AUDIO_MSF, (LPVOID)PlayAudioPtr, sizeof(CDROM_PLAY_AUDIO_MSF), NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* GetCdromSubQData * * DeviceHandle - Handle to CDRom device to retrieve information from. * SubQPtr - A pointer to a SUB_Q_CHANNEL_DATA structure to be filled in by * this routine. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD GetCdromSubQData( HANDLE DeviceHandle, PSUB_Q_CHANNEL_DATA SubQDataPtr, PCDROM_SUB_Q_DATA_FORMAT SubQFormatPtr ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_READ_Q_CHANNEL, (LPVOID)SubQFormatPtr, sizeof(CDROM_SUB_Q_DATA_FORMAT), (LPVOID)SubQDataPtr, sizeof(SUB_Q_CHANNEL_DATA), &bytesRead, NULL )) {
return ERROR_SUCCESS;
} else { return GetLastError(); } }
/******************************Public*Routine******************************\
* SeekCdrom * * This routine seek to an MSF address on the audio CD and enters * a hold (paused) state. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD SeekCdrom( HANDLE DeviceHandle, PCDROM_SEEK_AUDIO_MSF SeekAudioPtr ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_SEEK_AUDIO_MSF, (LPVOID)SeekAudioPtr, sizeof(CDROM_SEEK_AUDIO_MSF), NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* EjectCdrom * * This routine will eject a disc from a CDRom device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD EjectCdrom( HANDLE DeviceHandle ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_EJECT_MEDIA, NULL, 0, NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS; } else { return GetLastError(); } }
/******************************Public*Routine******************************\
* TestUnitReadyCdrom * * This routine will retrieve the status of the CDRom device. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD TestUnitReadyCdrom( HANDLE DeviceHandle ) { DWORD bytesRead;
if (DeviceIoControl( DeviceHandle, IOCTL_DISK_CHECK_VERIFY, NULL, 0, NULL, 0, &bytesRead, NULL )) {
return ERROR_SUCCESS;
} else { return GetLastError(); } }
#if 0
/******************************Public*Routine******************************\
* GetCdromVolume * * This routine will get the table of contents from * a CDRom device. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ DWORD GetCdromVolume( HANDLE DeviceHandle ) { DWORD bytesRead; VOLUME_CONTROL vc; TCHAR buff[80];
ZeroMemory( &vc, sizeof(vc) );
if (DeviceIoControl( DeviceHandle, IOCTL_CDROM_GET_VOLUME, NULL, 0, (LPVOID)&vc, sizeof(vc), &bytesRead, NULL )) {
wsprintf( buff, "%d %d %d %d\n", vc.PortVolume[0], vc.PortVolume[1], vc.PortVolume[2], vc.PortVolume[3] ); OutputDebugString( buff );
return ERROR_SUCCESS; } else { return GetLastError(); }
} #endif
#endif
|