/******************************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 #include #include #include #include /* required for all Windows applications */ #include #include #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