Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2714 lines
70 KiB

  1. /*
  2. * cdio.c
  3. *
  4. *
  5. * This module provides a C interface to the CD-ROM device
  6. * drivers to make the audio control a little simpler for
  7. * the rest of the driver.
  8. *
  9. * 21-Jun-91 NigelT
  10. * 10-Mar-92 RobinSp - Catch up with windows 3.1
  11. *
  12. * Copyright (c) 1990-1998 Microsoft Corporation
  13. *
  14. */
  15. #include <windows.h>
  16. #include <devioctl.h>
  17. #include <mmsystem.h>
  18. #include <tchar.h>
  19. #include "mcicda.h"
  20. #include "cda.h"
  21. #include "cdio.h"
  22. //#include <ntstatus.h>
  23. #ifndef STATUS_VERIFY_REQUIRED
  24. #define STATUS_VERIFY_REQUIRED ((DWORD)0x80000016L)
  25. #endif
  26. //
  27. // Private constants
  28. //
  29. //
  30. // Local functions (cd prefix, globals have Cd)
  31. //
  32. HANDLE cdOpenDeviceDriver(TCHAR szAnsiDeviceName, DWORD Access);
  33. void cdCloseDeviceDriver(HANDLE hDevice);
  34. DWORD cdGetDiskInfo(LPCDINFO lpInfo);
  35. DWORD cdIoctl(LPCDINFO lpInfo, DWORD Request, PVOID lpData, DWORD size);
  36. DWORD cdIoctlData(LPCDINFO lpInfo, DWORD Request, PVOID lpData,
  37. DWORD size, PVOID lpOutput, DWORD OutputSize);
  38. void CdSetAudioStatus (HCD hCD, UCHAR fStatus);
  39. BOOL CdGetAudioStatus (HCD hCD, UCHAR fStatus, DWORD * pStatus);
  40. BOOL CdGetTrack(LPCDINFO lpInfo, MSF msfPos, UCHAR * pTrack, MSF * pmsfStart);
  41. /***************************************************************************
  42. @doc EXTERNAL
  43. @api MSF | CdFindAudio | Given a position to start playing find
  44. the next audio track (if this one isn't) if any.
  45. @parm LPCDINFO | lpInfo | Pointer to CD info including track data.
  46. @parm MSF | msfStart | Position to start looking.
  47. @rdesc A new MSF to play from / seek to within an audio track or
  48. the end of the CD if none was found.
  49. ***************************************************************************/
  50. MSF CdFindAudio(LPCDINFO lpInfo, MSF msfStart)
  51. {
  52. UINT tracknum;
  53. MSF lastaudio = lpInfo->msfEnd;
  54. //
  55. // If we don't have a valid TOC then just obey - they may know
  56. // what they're doing.
  57. //
  58. dprintf2(("CdFindAudio"));
  59. if (!lpInfo->bTOCValid) {
  60. dprintf2(("CdFindAudio - No valid TOC"));
  61. return msfStart;
  62. }
  63. //
  64. // If we're being asked to play a data track then move forward
  65. // to the next audio track if there is one
  66. //
  67. //
  68. // Search for the track which ends after ours and is audio
  69. //
  70. for (tracknum = 0; ;tracknum++) {
  71. //
  72. // Note that some CDs return positions outside the playing range
  73. // sometimes (notably 0) so msfStart may be less than the first
  74. // track start
  75. //
  76. //
  77. // If we're beyond the start of the track and before the start
  78. // of the next track then this is the track we want.
  79. //
  80. // We assume we're always beyond the start of the first track
  81. // and we check that if we're looking at the last track then
  82. // we check we're before the end of the CD.
  83. //
  84. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK)) {
  85. // Remember the last audio track. The MCI CDAudio spec
  86. // for Seek to end says we position at the last audio track
  87. // which is not necessarily the last track on the disc
  88. lastaudio = lpInfo->Track[tracknum].msfStart;
  89. }
  90. if ((msfStart >= lpInfo->Track[tracknum].msfStart || tracknum == 0)
  91. &&
  92. #ifdef OLD
  93. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack &&
  94. msfStart < lpInfo->msfEnd ||
  95. tracknum + lpInfo->FirstTrack != lpInfo->LastTrack &&
  96. msfStart < lpInfo->Track[tracknum + 1].msfStart)) {
  97. #else
  98. // Simplify the logic. When reviewed to the extent that the
  99. // reviewer is convinced the test below is identical to the
  100. // test above the old code can be deleted.
  101. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack
  102. ? msfStart <= lpInfo->msfEnd
  103. : msfStart < lpInfo->Track[tracknum + 1].msfStart)
  104. ) {
  105. #endif
  106. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK)) {
  107. return msfStart;
  108. }
  109. //
  110. // Move to next track if there is one and this one is a
  111. // data track
  112. //
  113. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack) {
  114. //
  115. // Didn't find a suitable start point so return end of CD
  116. //
  117. return lpInfo->msfEnd;
  118. } else {
  119. //
  120. // We don't get here if this was already the last track
  121. //
  122. msfStart = lpInfo->Track[tracknum + 1].msfStart;
  123. }
  124. }
  125. //
  126. // Exhausted all tracks ?
  127. //
  128. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack) {
  129. return lastaudio;
  130. }
  131. }
  132. }
  133. /***************************************************************************
  134. @doc EXTERNAL
  135. @api WORD | CdGetNumDrives | Get the number of CD-ROM drives in
  136. the system.
  137. @rdesc The return value is the number of drives available.
  138. @comm It is assumed that all CD-ROM drives have audio capability,
  139. but this may not be true and consequently later calls which
  140. try to play audio CDs on those drives may fail. It takes a
  141. fairly bad user to put an audio CD in a drive not connected
  142. up to play audio.
  143. ***************************************************************************/
  144. int CdGetNumDrives(void)
  145. {
  146. TCHAR cDrive;
  147. LPCDINFO lpInfo;
  148. TCHAR szName[ANSI_NAME_SIZE];
  149. DWORD dwLogicalDrives;
  150. dprintf2(("CdGetNumDrives"));
  151. if (NumDrives == 0) {
  152. //
  153. // We start with the name A: and work up to Z: or until we have
  154. // accumulated MCIRBOOK_MAX_DRIVES drives.
  155. //
  156. lpInfo = CdInfo;
  157. lstrcpy(szName, TEXT("?:\\"));
  158. for (cDrive = TEXT('A'), dwLogicalDrives = GetLogicalDrives();
  159. NumDrives < MCIRBOOK_MAX_DRIVES && cDrive <= TEXT('Z');
  160. cDrive++) {
  161. szName[0] = cDrive;
  162. if (dwLogicalDrives & (1 << (cDrive - TEXT('A'))) &&
  163. GetDriveType(szName) == DRIVE_CDROM)
  164. {
  165. lpInfo->cDrive = cDrive;
  166. NumDrives++;
  167. lpInfo++; // Move on to next device info structure
  168. dprintf2(("CdGetNumDrives - %c: is a CDROM drive", cDrive));
  169. }
  170. }
  171. }
  172. return NumDrives;
  173. }
  174. /***************************************************************************
  175. @doc EXTERNAL
  176. @api HCD | CdOpen | Open a drive.
  177. @parm int | Drive | The drive number to open.
  178. @rdesc
  179. If the drive exists and is available then the return value is TRUE.
  180. If no drive exists, it is unavavilable, already open or an error
  181. occurs then the return value is set to FALSE.
  182. @comm
  183. The CdInfo slot for this drive is initialized if the open is
  184. successful.
  185. ***************************************************************************/
  186. BOOL CdOpen(int Drive)
  187. {
  188. LPCDINFO lpInfo;
  189. TCHAR szName[ANSI_NAME_SIZE];
  190. //
  191. // Check the drive number is valid
  192. //
  193. if (Drive > NumDrives || Drive < 0) {
  194. dprintf1(("Drive %u is invalid", Drive));
  195. return FALSE;
  196. }
  197. lpInfo = &CdInfo[Drive];
  198. //
  199. // See if it's already open
  200. // CONSIDER: Do shareable support code here
  201. //
  202. if (lpInfo->hDevice != NULL) {
  203. dprintf2(("Drive %u (%c) is being opened recursively - %d users",
  204. Drive, (char)(lpInfo->cDrive), lpInfo->NumberOfUsers + 1));
  205. lpInfo->NumberOfUsers++;
  206. return TRUE;
  207. }
  208. //
  209. // Make sure it really is a CDROM drive
  210. //
  211. lstrcpy(szName, TEXT("?:\\"));
  212. szName[0] = lpInfo->cDrive;
  213. if (GetDriveType(szName) != DRIVE_CDROM)
  214. {
  215. dprintf2(("CdOpen - Error, Drive %u, Letter = %c: is not a CDROM drive",
  216. Drive, (char)(lpInfo->cDrive)));
  217. return FALSE;
  218. }
  219. //
  220. // open the device driver
  221. //
  222. lpInfo->hDevice = cdOpenDeviceDriver(lpInfo->cDrive, GENERIC_READ);
  223. if (lpInfo->hDevice == NULL) {
  224. dprintf2(("Failed to open %c:", (char)(lpInfo->cDrive)));
  225. return FALSE;
  226. }
  227. //
  228. // reset the TOC valid indicator
  229. //
  230. lpInfo->bTOCValid = FALSE;
  231. lpInfo->fPrevStatus = 0;
  232. lpInfo->fPrevSeekTime = 0;
  233. lpInfo->VolChannels[0] = 0xFF;
  234. lpInfo->VolChannels[1] = 0xFF;
  235. lpInfo->VolChannels[2] = 0xFF;
  236. lpInfo->VolChannels[3] = 0xFF;
  237. //
  238. // Device now in use
  239. //
  240. lpInfo->NumberOfUsers = 1;
  241. #if 0 // unnecessary and slows down media player startup
  242. //
  243. // Get the TOC if it's available (helps with the problems with the
  244. // Pioneer DRM-600 drive not being ready until the TOC has been read).
  245. //
  246. cdGetDiskInfo(lpInfo);
  247. #endif
  248. return TRUE;
  249. }
  250. /***************************************************************************
  251. @doc EXTERNAL
  252. @api BOOL | CdClose | Close a drive.
  253. @parm HCD | hCD | The handle of a currently open drive.
  254. @rdesc The return value is TRUE if the drive is closed, FALSE
  255. if the drive was not open or some other error occured.
  256. ***************************************************************************/
  257. BOOL CdClose(HCD hCD)
  258. {
  259. LPCDINFO lpInfo;
  260. lpInfo = (LPCDINFO) hCD;
  261. dprintf2(("CdClose(%08XH)", hCD));
  262. if (lpInfo == NULL) {
  263. dprintf1(("CdClose, NULL info pointer"));
  264. return FALSE;
  265. }
  266. lpInfo->fPrevStatus = 0;
  267. if (lpInfo->hDevice == NULL) {
  268. dprintf1(("CdClose, Attempt to close unopened device"));
  269. return FALSE;
  270. }
  271. if (lpInfo->NumberOfUsers == 0)
  272. {
  273. dprintf2(("CdClose (%c), number of users already = 0",
  274. (char)(lpInfo->cDrive)));
  275. }
  276. else if (--lpInfo->NumberOfUsers == 0)
  277. {
  278. cdCloseDeviceDriver(lpInfo->hDevice);
  279. lpInfo->hDevice = (HANDLE) 0;
  280. }
  281. else
  282. {
  283. dprintf2(("CdClose (%c), Device still open with %d users",
  284. (char)(lpInfo->cDrive), lpInfo->NumberOfUsers));
  285. }
  286. return TRUE;
  287. }
  288. /***************************************************************************
  289. @doc EXTERNAL
  290. @api BOOL | CdReload | Reload Device
  291. @parm HCD | hCD | The handle of a currently open drive.
  292. @rdesc The return value is TRUE if the drive tray is reloaded
  293. ***************************************************************************/
  294. BOOL CdReload (LPCDINFO lpInfo)
  295. {
  296. DWORD ii;
  297. DWORD index;
  298. HANDLE hNewDevice;
  299. if (!lpInfo)
  300. {
  301. dprintf2(("CdReload, NULL info pointer"));
  302. return FALSE;
  303. }
  304. //
  305. // Reload Device
  306. // Note: Don't close old device til we have a new device
  307. // so we don't hose any users out there
  308. //
  309. EnterCrit (lpInfo->DeviceCritSec);
  310. // Make sure we have an open device
  311. if (NULL == lpInfo->hDevice)
  312. {
  313. dprintf2(("CdReload, Attempt to reload unopened device"));
  314. LeaveCrit (lpInfo->DeviceCritSec);
  315. return FALSE;
  316. }
  317. // Open New Device
  318. hNewDevice = cdOpenDeviceDriver(lpInfo->cDrive, GENERIC_READ);
  319. if (NULL == hNewDevice)
  320. {
  321. dprintf2(("CdReload, Failed to reload driver"));
  322. LeaveCrit (lpInfo->DeviceCritSec);
  323. return FALSE;
  324. }
  325. // Close Old Device
  326. cdCloseDeviceDriver(lpInfo->hDevice);
  327. // Assign New Device
  328. lpInfo->hDevice = hNewDevice;
  329. //lpInfo->fPrevStatus = 0;
  330. //
  331. // reset the TOC valid indicator
  332. //
  333. lpInfo->bTOCValid = FALSE;
  334. LeaveCrit (lpInfo->DeviceCritSec);
  335. // Succesfully Reloaded
  336. return TRUE;
  337. } // End CdReload
  338. /***************************************************************************
  339. @doc EXTERNAL
  340. @api BOOL | CdReady | Test if a CD is ready.
  341. @parm HCD | hCD | The handle of a currently open drive.
  342. @rdesc The return value is TRUE if the drive has a disk in it
  343. and we have read the TOC. It is FALSE if the drive is not
  344. ready or we cannot read the TOC.
  345. ***************************************************************************/
  346. BOOL CdReady(HCD hCD)
  347. {
  348. LPCDINFO lpInfo;
  349. dprintf2(("CdReady(%08XH)", hCD));
  350. lpInfo = (LPCDINFO) hCD;
  351. //
  352. // Check a disk is in the drive and the door is shut and
  353. // we have a valid table of contents
  354. //
  355. return ERROR_SUCCESS == cdIoctl(lpInfo,
  356. IOCTL_CDROM_CHECK_VERIFY,
  357. NULL,
  358. 0);
  359. }
  360. /***************************************************************************
  361. @doc EXTERNAL
  362. @api BOOL | CdTrayClosed | Test what state a CD is in.
  363. @parm HCD | hCD | The handle of a currently open drive.
  364. @rdesc The return value is TRUE if the drive tray is closed
  365. ***************************************************************************/
  366. BOOL CdTrayClosed(HCD hCD)
  367. {
  368. LPCDINFO lpInfo;
  369. DWORD dwError;
  370. dprintf2(("CdTrayClosed(%08XH)", hCD));
  371. lpInfo = (LPCDINFO) hCD;
  372. //
  373. // Check a disk is in the drive and the door is shut.
  374. //
  375. dwError = cdIoctl(lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  376. switch (dwError)
  377. {
  378. case ERROR_NO_MEDIA_IN_DRIVE:
  379. case ERROR_UNRECOGNIZED_MEDIA:
  380. case ERROR_NOT_READY:
  381. return FALSE;
  382. default:
  383. return TRUE;
  384. }
  385. }
  386. /***************************************************************************
  387. @doc INTERNAL
  388. @api DWORD | cdGetDiskInfo | Read the disk info and TOC
  389. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  390. @rdesc The return value is ERROR_SUCCESS if the info is read ok,
  391. otherwise the NT status code if not.
  392. ***************************************************************************/
  393. DWORD cdGetDiskInfo(LPCDINFO lpInfo)
  394. {
  395. CDROM_TOC Toc;
  396. int i;
  397. PTRACK_DATA pTocTrack;
  398. LPTRACK_INFO pLocalTrack;
  399. DWORD Status;
  400. UCHAR TrackNumber;
  401. dprintf2(("cdGetDiskInfo(%08XH)", lpInfo));
  402. #if 0 // If the app doesn't poll we may miss a disk change
  403. //
  404. // If the TOC is valid already then don't read it
  405. //
  406. if (lpInfo->bTOCValid) {
  407. return TRUE;
  408. }
  409. #endif
  410. //
  411. // Read the table of contents (TOC)
  412. //
  413. FillMemory(&Toc, sizeof(Toc), 0xFF);
  414. Status = cdIoctl(lpInfo, IOCTL_CDROM_READ_TOC, &Toc, sizeof(Toc));
  415. if (ERROR_SUCCESS != Status) {
  416. dprintf2(("cdGetDiskInfo - Failed to read TOC"));
  417. return Status;
  418. }
  419. #ifdef DBGG
  420. dprintf4((" TOC..."));
  421. dprintf4((" Length[0] %02XH", Toc.Length[0]));
  422. dprintf4((" Length[1] %02XH", Toc.Length[1]));
  423. dprintf4((" FirstTrack %u", Toc.FirstTrack));
  424. dprintf4((" LastTrack %u", Toc.LastTrack));
  425. dprintf4((" Track info..."));
  426. for (i=0; i<20; i++) {
  427. dprintf4((" Track: %03u, Ctrl: %02XH, MSF: %02d %02d %02d",
  428. Toc.TrackData[i].TrackNumber,
  429. Toc.TrackData[i].Control,
  430. Toc.TrackData[i].Address[1],
  431. Toc.TrackData[i].Address[2],
  432. Toc.TrackData[i].Address[3]));
  433. }
  434. #endif
  435. //
  436. // Avoid problems with bad CDs
  437. //
  438. if (Toc.FirstTrack == 0) {
  439. return ERROR_INVALID_DATA;
  440. }
  441. if (Toc.LastTrack > MAXIMUM_NUMBER_TRACKS - 1) {
  442. Toc.LastTrack = MAXIMUM_NUMBER_TRACKS - 1;
  443. }
  444. //
  445. // hide the data track on Enhanced CDs (CD+).
  446. //
  447. for (i=0; i < (Toc.LastTrack - Toc.FirstTrack + 1); i++) {
  448. if (Toc.TrackData[i].Control & AUDIO_DATA_TRACK) {
  449. //
  450. // if first track, just exit out.
  451. //
  452. if (i == 0) {
  453. i = Toc.LastTrack+1;
  454. } else {
  455. //
  456. // remove one track from the TOC
  457. //
  458. Toc.LastTrack -= 1;
  459. //
  460. // knock 2.5 minutes off the current track to
  461. // hide the final leadin and make it the lead-out
  462. // track
  463. //
  464. Toc.TrackData[i].Address[1] -= 2;
  465. Toc.TrackData[i].Address[2] += 30;
  466. if (Toc.TrackData[i].Address[2] < 60) {
  467. Toc.TrackData[i].Address[1] -= 1;
  468. } else {
  469. Toc.TrackData[i].Address[2] -= 60;
  470. }
  471. Toc.TrackData[i].TrackNumber = 0xAA;
  472. }
  473. }
  474. }
  475. #ifdef DBGG
  476. dprintf4((" TOC (munged)..."));
  477. dprintf4((" Length[0] %02XH", Toc.Length[0]));
  478. dprintf4((" Length[1] %02XH", Toc.Length[1]));
  479. dprintf4((" FirstTrack %u", Toc.FirstTrack));
  480. dprintf4((" LastTrack %u", Toc.LastTrack));
  481. dprintf4((" Track info..."));
  482. for (i=0; i<20; i++) {
  483. dprintf4((" Track: %03u, Ctrl: %02XH, MSF: %02d %02d %02d",
  484. Toc.TrackData[i].TrackNumber,
  485. Toc.TrackData[i].Control,
  486. Toc.TrackData[i].Address[1],
  487. Toc.TrackData[i].Address[2],
  488. Toc.TrackData[i].Address[3]));
  489. }
  490. #endif
  491. //
  492. // Copy the data we got back to our own cache in the format
  493. // we like it. We copy all the tracks and then use the next track
  494. // data as the end of the disk. (Lead out info).
  495. //
  496. lpInfo->FirstTrack = Toc.FirstTrack;
  497. lpInfo->LastTrack = Toc.LastTrack;
  498. pTocTrack = &Toc.TrackData[0];
  499. pLocalTrack = &(lpInfo->Track[0]);
  500. TrackNumber = lpInfo->FirstTrack;
  501. while (TrackNumber <= Toc.LastTrack) {
  502. pLocalTrack->TrackNumber = TrackNumber;
  503. if (TrackNumber != pTocTrack->TrackNumber) {
  504. dprintf2(("Track data not correct in TOC"));
  505. return ERROR_INVALID_DATA;
  506. }
  507. pLocalTrack->msfStart = MAKERED(pTocTrack->Address[1],
  508. pTocTrack->Address[2],
  509. pTocTrack->Address[3]);
  510. pLocalTrack->Ctrl = pTocTrack->Control;
  511. pTocTrack++;
  512. pLocalTrack++;
  513. TrackNumber++;
  514. }
  515. //
  516. // Save the leadout for the disc id algorithm
  517. //
  518. lpInfo->leadout = MAKERED(pTocTrack->Address[1],
  519. pTocTrack->Address[2],
  520. pTocTrack->Address[3]);
  521. //
  522. // Some CD Rom drives don't like to go right to the end
  523. // so we fake it to be 1 frames earlier
  524. //
  525. lpInfo->msfEnd = reddiff(lpInfo->leadout, 1);
  526. lpInfo->bTOCValid = TRUE;
  527. return ERROR_SUCCESS;
  528. }
  529. /***************************************************************************
  530. @doc INTERNAL
  531. @api HANDLE | cdOpenDeviceDriver | Open a device driver.
  532. @parm LPSTR | szAnsiDeviceName | The name of the device to open.
  533. @parm DWORD | Access | Access to use to open the file. This
  534. will be one of:
  535. GENERIC_READ if we want to actually operate the device
  536. FILE_READ_ATTRIBTES if we just want to see if it's there. This
  537. prevents the device from being mounted and speeds things up.
  538. @rdesc The return value is the handle to the open device or
  539. NULL if the device driver could not be opened.
  540. ***************************************************************************/
  541. HANDLE cdOpenDeviceDriver(TCHAR cDrive, DWORD Access)
  542. {
  543. HANDLE hDevice;
  544. TCHAR szDeviceName[7]; // "\\\\.\\?:"
  545. DWORD dwErr;
  546. dprintf2(("cdOpenDeviceDriver"));
  547. wsprintf(szDeviceName, TEXT("\\\\.\\%c:"), cDrive);
  548. //
  549. // have a go at opening the driver
  550. //
  551. {
  552. UINT OldErrorMode;
  553. //
  554. // We don't want to see hard error popups
  555. //
  556. OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  557. hDevice = CreateFile( szDeviceName,
  558. Access,
  559. FILE_SHARE_READ|FILE_SHARE_WRITE,
  560. NULL,
  561. OPEN_EXISTING,
  562. FILE_ATTRIBUTE_NORMAL,
  563. NULL );
  564. if (hDevice == INVALID_HANDLE_VALUE) {
  565. hDevice = (HANDLE) 0;
  566. dwErr = GetLastError ();
  567. dprintf1(("cdOpenDeviceDriver - Failed to open device driver %c: code %d", cDrive, dwErr));
  568. }
  569. //
  570. // Restore the error mode
  571. //
  572. SetErrorMode(OldErrorMode);
  573. }
  574. return hDevice;
  575. }
  576. /***************************************************************************
  577. @doc INTERNAL
  578. @api void | cdCloseDeviceDriver | Close a device driver.
  579. @parm HANDLE | hDevice | Handle of the device to close.
  580. @rdesc There is no return value.
  581. ***************************************************************************/
  582. void cdCloseDeviceDriver(HANDLE hDevice)
  583. {
  584. DWORD status;
  585. dprintf2(("cdCloseDeviceDriver"));
  586. if (hDevice == NULL) {
  587. dprintf1(("Attempt to close NULL handle"));
  588. }
  589. status = CloseHandle(hDevice);
  590. if (!status) {
  591. dprintf1(("cdCloseDeviceDriver - Failed to close device. Error %08XH", GetLastError()));
  592. }
  593. }
  594. /***************************************************************************
  595. @doc INTERNAL
  596. @api DWORD | cdIoctl | Send an IOCTL request to the device driver.
  597. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  598. @parm DWORD | Request | The IOCTL request code.
  599. @parm PVOID | lpData | Pointer to a data structure to be passed.
  600. @parm DWORD | dwSize | The length of the data strucure.
  601. @comm This function returns the disk status
  602. @rdesc The return value is the status value returned from the
  603. call to DeviceIoControl
  604. ***************************************************************************/
  605. DWORD cdIoctl(LPCDINFO lpInfo, DWORD Request, PVOID lpData, DWORD dwSize)
  606. {
  607. DWORD Status;
  608. Status = cdIoctlData(lpInfo, Request, lpData, dwSize, lpData, dwSize);
  609. //if (ERROR_SUCCESS != Status && Request == IOCTL_CDROM_CHECK_VERIFY) {
  610. // lpInfo->bTOCValid = FALSE;
  611. //}
  612. return Status;
  613. }
  614. /***************************************************************************
  615. @doc INTERNAL
  616. @api DWORD | cdIoctlData | Send an IOCTL request to the device driver.
  617. @parm LPCDINFO | lpInfo | Pointer to a CDINFO structure.
  618. @parm DWORD | Request | The IOCTL request code.
  619. @parm PVOID | lpData | Pointer to a data structure to be passed.
  620. @parm DWORD | dwSize | The length of the data strucure.
  621. @parm PVOID | lpOutput | Our output data
  622. @parm DWORD | OutputSize | Our output data (maximum) size
  623. @comm This function returns the disk status
  624. @rdesc The return value is the status value returned from the
  625. call to DeviceIoControl
  626. ***************************************************************************/
  627. DWORD cdIoctlData(LPCDINFO lpInfo, DWORD Request, PVOID lpData,
  628. DWORD dwSize, PVOID lpOutput, DWORD OutputSize)
  629. {
  630. BOOL status;
  631. UINT OldErrorMode;
  632. DWORD BytesReturned;
  633. DWORD dwError = ERROR_SUCCESS;
  634. BOOL fTryAgain;
  635. dprintf3(("cdIoctl(%08XH, %08XH, %08XH, %08XH", lpInfo, Request, lpData, dwSize));
  636. if (!lpInfo->hDevice) {
  637. dprintf1(("cdIoctlData - Device not open"));
  638. return ERROR_INVALID_FUNCTION;
  639. }
  640. #if DBG
  641. switch (Request) {
  642. case IOCTL_CDROM_READ_TOC:
  643. dprintf3(("IOCTL_CDROM_READ_TOC"));
  644. break;
  645. case IOCTL_CDROM_GET_CONTROL:
  646. dprintf3(("IOCTL_CDROM_GET_CONTROL"));
  647. break;
  648. case IOCTL_CDROM_PLAY_AUDIO_MSF:
  649. dprintf3(("IOCTL_CDROM_PLAY_AUDIO_MSF"));
  650. break;
  651. case IOCTL_CDROM_SEEK_AUDIO_MSF:
  652. dprintf3(("IOCTL_CDROM_SEEK_AUDIO_MSF"));
  653. break;
  654. case IOCTL_CDROM_STOP_AUDIO:
  655. dprintf3(("IOCTL_CDROM_STOP_AUDIO"));
  656. break;
  657. case IOCTL_CDROM_PAUSE_AUDIO:
  658. dprintf3(("IOCTL_CDROM_PAUSE_AUDIO"));
  659. break;
  660. case IOCTL_CDROM_RESUME_AUDIO:
  661. dprintf3(("IOCTL_CDROM_RESUME_AUDIO"));
  662. break;
  663. case IOCTL_CDROM_GET_VOLUME:
  664. dprintf3(("IOCTL_CDROM_SET_VOLUME"));
  665. break;
  666. case IOCTL_CDROM_SET_VOLUME:
  667. dprintf3(("IOCTL_CDROM_GET_VOLUME"));
  668. break;
  669. case IOCTL_CDROM_READ_Q_CHANNEL:
  670. dprintf3(("IOCTL_CDROM_READ_Q_CHANNEL"));
  671. break;
  672. case IOCTL_CDROM_CHECK_VERIFY:
  673. dprintf3(("IOCTL_CDROM_CHECK_VERIFY"));
  674. break;
  675. }
  676. #endif // DBG
  677. //
  678. // We don't want to see hard error popups
  679. //
  680. fTryAgain = TRUE;
  681. while (fTryAgain)
  682. {
  683. fTryAgain = FALSE;
  684. OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  685. status = DeviceIoControl(lpInfo->hDevice,
  686. Request,
  687. lpData,
  688. dwSize,
  689. lpOutput,
  690. OutputSize,
  691. &BytesReturned,
  692. NULL);
  693. //
  694. // Restore the error mode
  695. //
  696. SetErrorMode(OldErrorMode);
  697. // Check for Failure
  698. if (!status)
  699. {
  700. dwError = GetLastError();
  701. if (dwError == ERROR_MEDIA_CHANGED)
  702. {
  703. dprintf2(("Error Media Changed"));
  704. }
  705. //
  706. // Treat anything bad as invalidating our TOC. Some of the things
  707. // we call are invalid on some devices so don't count those. Also
  708. // the device can be 'busy' while playing so don't count that case
  709. // either.
  710. //
  711. if (Request == IOCTL_CDROM_CHECK_VERIFY)
  712. {
  713. lpInfo->bTOCValid = FALSE;
  714. switch (dwError)
  715. {
  716. case ERROR_MEDIA_CHANGED:
  717. dprintf2(("Error Media Changed, Reloading Device"));
  718. // Reload new Device
  719. if (CdReload (lpInfo))
  720. fTryAgain = TRUE;
  721. break;
  722. case ERROR_INVALID_FUNCTION:
  723. case ERROR_BUSY:
  724. default:
  725. break;
  726. }
  727. }
  728. #if DBG
  729. dprintf1(("IOCTL %8XH Status: %08XH", Request, dwError));
  730. #endif
  731. }
  732. } // End While (fTryAgain)
  733. return dwError;
  734. }
  735. /***************************************************************************
  736. @doc EXTERNAL
  737. @api BOOL | CdPlay | Play a section of the CD
  738. @parm HCD | hCD | The handle of a currently open drive.
  739. @parm MSF | msfStart | Where to start
  740. @parm MSF | msfEnd | Where to end
  741. @rdesc The return value is TRUE if the drive is play is started,
  742. FALSE if not.
  743. ***************************************************************************/
  744. BOOL CdPlay(HCD hCD, MSF msfStart, MSF msfEnd)
  745. {
  746. LPCDINFO lpInfo;
  747. CDROM_PLAY_AUDIO_MSF msfPlay;
  748. BOOL fResult;
  749. dprintf2(("CdPlay(%08XH, %08XH, %08XH)", hCD, msfStart, msfEnd));
  750. lpInfo = (LPCDINFO) hCD;
  751. msfStart = CdFindAudio(lpInfo, msfStart);
  752. //
  753. // If the start is now beyond the end then don't play anything
  754. //
  755. if (msfStart > msfEnd) {
  756. return TRUE;
  757. }
  758. //
  759. // Set up the data for the call to the driver
  760. //
  761. msfPlay.StartingM = REDMINUTE(msfStart);
  762. msfPlay.StartingS = REDSECOND(msfStart);
  763. msfPlay.StartingF = REDFRAME(msfStart);
  764. msfPlay.EndingM = REDMINUTE(msfEnd);
  765. msfPlay.EndingS = REDSECOND(msfEnd);
  766. msfPlay.EndingF = REDFRAME(msfEnd);
  767. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo,
  768. IOCTL_CDROM_PLAY_AUDIO_MSF,
  769. &msfPlay,
  770. sizeof(msfPlay)));
  771. if (fResult)
  772. {
  773. lpInfo->fPrevSeekTime = msfStart;
  774. }
  775. return fResult;
  776. }
  777. /***************************************************************************
  778. @doc EXTERNAL
  779. @api BOOL | CdSeek | Seek to a given part of the CD
  780. @parm HCD | hCD | The handle of a currently open drive.
  781. @parm MSF | msf | Where to seek to
  782. @rdesc The return value is TRUE if the seek is successful,
  783. FALSE if not.
  784. ***************************************************************************/
  785. BOOL CdSeek(HCD hCD, MSF msf, BOOL fForceAudio)
  786. {
  787. LPCDINFO lpInfo;
  788. CDROM_SEEK_AUDIO_MSF msfSeek;
  789. BOOL fResult;
  790. dprintf2(("CdSeek(%08XH, %08XH) Forcing search for audio: %d", hCD, msf, fForceAudio));
  791. lpInfo = (LPCDINFO) hCD;
  792. //
  793. // Only seek to audio
  794. //
  795. if (fForceAudio) { // On a seek to END or seek to START command
  796. msf = CdFindAudio(lpInfo, msf);
  797. dprintf2(("Cd Seek changed msf to %08XH", msf));
  798. } else {
  799. if (msf != CdFindAudio(lpInfo, msf)) {
  800. return TRUE;
  801. }
  802. }
  803. #if 1
  804. msfSeek.M = REDMINUTE(msf);
  805. msfSeek.S = REDSECOND(msf);
  806. msfSeek.F = REDFRAME(msf);
  807. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_SEEK_AUDIO_MSF,
  808. &msfSeek, sizeof(msfSeek)));
  809. if (fResult)
  810. {
  811. lpInfo->fPrevSeekTime = msf;
  812. }
  813. return fResult;
  814. #else
  815. //
  816. // This is a hideous hack to make more drives work. It uses the
  817. // method originally used by Cd player to seek - viz play from
  818. // the requested position and immediatly pause
  819. //
  820. return CdPlay(hCD, msf, redadd(lpInfo->msfEnd,1)) || CdPause(hCD);
  821. #endif
  822. }
  823. /***************************************************************************
  824. @doc EXTERNAL
  825. @api MSF | CdTrackStart | Get the start time of a track.
  826. @parm HCD | hCD | The handle of a currently open drive.
  827. @parm UCHAR | Track | The track number.
  828. @rdesc The return value is the start time of the track expressed
  829. in MSF or INVALID_TRACK if the track number is not in the TOC.
  830. ***************************************************************************/
  831. MSF CdTrackStart(HCD hCD, UCHAR Track)
  832. {
  833. LPCDINFO lpInfo;
  834. LPTRACK_INFO lpTrack;
  835. dprintf2(("CdTrackStart(%08XH, %u)", hCD, Track));
  836. lpInfo = (LPCDINFO) hCD;
  837. //
  838. // We may need to read the TOC because we're not doing it on open
  839. // any more
  840. //
  841. if (!lpInfo->bTOCValid && CdNumTracks(hCD) == 0) {
  842. dprintf1(("TOC not valid"));
  843. return INVALID_TRACK;
  844. }
  845. if ((Track < lpInfo->FirstTrack) || (Track > lpInfo->LastTrack)) {
  846. dprintf1(("Track number out of range"));
  847. return INVALID_TRACK;
  848. }
  849. // search for the track info in the TOC
  850. lpTrack = lpInfo->Track;
  851. while (lpTrack->TrackNumber != Track) lpTrack++;
  852. return lpTrack->msfStart;
  853. }
  854. /***************************************************************************
  855. @doc EXTERNAL
  856. @api MSF | CdTrackLength | Get the length of a track.
  857. @parm HCD | hCD | The handle of a currently open drive.
  858. @parm UCHAR | Track | The track number.
  859. @rdesc The return value is the start time of the track expressed
  860. in MSF or INVALID_TRACK if the track number is not in the TOC.
  861. ***************************************************************************/
  862. MSF CdTrackLength(HCD hCD, UCHAR Track)
  863. {
  864. LPCDINFO lpInfo;
  865. MSF TrackStart;
  866. MSF NextTrackStart;
  867. lpInfo = (LPCDINFO) hCD;
  868. dprintf2(("CdTrackLength(%08XH, %u)", hCD, Track));
  869. //
  870. // Get the start of this track
  871. //
  872. TrackStart = CdTrackStart(hCD, Track);
  873. if (TrackStart == INVALID_TRACK) {
  874. return INVALID_TRACK;
  875. }
  876. if (Track == lpInfo->LastTrack) {
  877. return reddiff(lpInfo->msfEnd, TrackStart);
  878. } else {
  879. NextTrackStart = CdTrackStart(hCD, (UCHAR)(Track + 1));
  880. if (NextTrackStart == INVALID_TRACK) {
  881. return INVALID_TRACK;
  882. } else {
  883. return reddiff(NextTrackStart, TrackStart);
  884. }
  885. }
  886. }
  887. /***************************************************************************
  888. @doc EXTERNAL
  889. @api MSF | CdTrackType | Get the type of a track.
  890. @parm HCD | hCD | The handle of a currently open drive.
  891. @parm UCHAR | Track | The track number.
  892. @rdesc The return value is either MCI_TRACK_AUDIO or MCI_TRACK_OTHER.
  893. ***************************************************************************/
  894. int CdTrackType(HCD hCD, UCHAR Track)
  895. {
  896. LPCDINFO lpInfo;
  897. lpInfo = (LPCDINFO) hCD;
  898. dprintf2(("CdTrackType(%08XH, %u)", hCD, Track));
  899. if ( INVALID_TRACK == CdTrackStart(hCD, (UCHAR)Track) ) {
  900. return INVALID_TRACK;
  901. }
  902. if ( lpInfo->Track[Track-lpInfo->FirstTrack].Ctrl & IS_DATA_TRACK) {
  903. return MCI_CDA_TRACK_OTHER;
  904. }
  905. return MCI_CDA_TRACK_AUDIO;
  906. }
  907. /***************************************************************************
  908. @doc EXTERNAL
  909. @api MSF | CdPosition | Get the current position.
  910. @parm HCD | hCD | The handle of a currently open drive.
  911. @parm MSF * | tracktime | position in MSF (track relative)
  912. @parm MSF * | disktime | position in MSF (disk relative)
  913. @rdesc TRUE if position returned correctly (in tracktime and disktime).
  914. FALSE otherwise.
  915. If the device does not support query of the position then
  916. we return position 0.
  917. ***************************************************************************/
  918. BOOL CdPosition(HCD hCD, MSF *tracktime, MSF *disktime)
  919. {
  920. LPCDINFO lpInfo;
  921. SUB_Q_CHANNEL_DATA sqd;
  922. CDROM_SUB_Q_DATA_FORMAT Format;
  923. MSF msfPos;
  924. int tries;
  925. DWORD dwReturn;
  926. UCHAR fStatus;
  927. UCHAR fCode;
  928. UCHAR cTrack;
  929. dprintf2(("CdPosition(%08XH)", hCD));
  930. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  931. lpInfo = (LPCDINFO) hCD;
  932. for (tries=0; tries<10; tries++)
  933. {
  934. memset(&sqd, 0xFF, sizeof(sqd));
  935. dwReturn = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  936. &Format, sizeof(Format), &sqd, sizeof(sqd));
  937. switch (dwReturn)
  938. {
  939. case ERROR_SUCCESS:
  940. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  941. fCode = sqd.CurrentPosition.FormatCode;
  942. cTrack = sqd.CurrentPosition.TrackNumber;
  943. // Save previous audio status to prevent bug
  944. CdSetAudioStatus (hCD, fStatus);
  945. // If the track > 100 (outside spec'ed range)
  946. // OR track > last track number
  947. // then display an error message
  948. if ((fCode == 0x01) &&
  949. ( (100 < cTrack) ||
  950. ((lpInfo->bTOCValid) && (lpInfo->LastTrack < cTrack))) &&
  951. (tries<9)) {
  952. // Always display this message on checked builds.
  953. // We need some feeling for how often this happens
  954. // It should NEVER happen, but (at least for NEC
  955. // drives) we see it after a seek to end
  956. dprintf1(("CDIoctlData returned track==%d, retrying", cTrack));
  957. continue;
  958. }
  959. break;
  960. case ERROR_INVALID_FUNCTION:
  961. dprintf2(("CdPositon - Error Invalid Function"));
  962. *tracktime = REDTH(0, 1);
  963. *disktime = REDTH(0, 0);
  964. return TRUE;
  965. default:
  966. dprintf1(("CdPosition - Failed to get Q channel data"));
  967. return FALSE;
  968. }
  969. dprintf4(("Status = %02X, Length[0] = %02X, Length[1] = %02X",
  970. fStatus,
  971. sqd.CurrentPosition.Header.DataLength[0],
  972. sqd.CurrentPosition.Header.DataLength[1]));
  973. dprintf4((" Format %02XH", fCode));
  974. dprintf4((" Absolute Address %02X%02X%02X%02XH",
  975. sqd.CurrentPosition.AbsoluteAddress[0],
  976. sqd.CurrentPosition.AbsoluteAddress[1],
  977. sqd.CurrentPosition.AbsoluteAddress[2],
  978. sqd.CurrentPosition.AbsoluteAddress[3]));
  979. dprintf4((" Relative Address %02X%02X%02X%02XH",
  980. sqd.CurrentPosition.TrackRelativeAddress[0],
  981. sqd.CurrentPosition.TrackRelativeAddress[1],
  982. sqd.CurrentPosition.TrackRelativeAddress[2],
  983. sqd.CurrentPosition.TrackRelativeAddress[3]));
  984. if (fCode == 0x01) { // MSF format ?
  985. // data is current position
  986. msfPos = MAKERED(sqd.CurrentPosition.AbsoluteAddress[1],
  987. sqd.CurrentPosition.AbsoluteAddress[2],
  988. sqd.CurrentPosition.AbsoluteAddress[3]);
  989. if (msfPos == 0)
  990. {
  991. //
  992. // If position is 0 (this seems to happen on the Toshiba) then
  993. // we'll return our last valid seek position
  994. //
  995. MSF msfStart;
  996. MSF msfRel;
  997. msfPos = lpInfo->fPrevSeekTime;
  998. if (CdGetTrack (lpInfo, msfPos, &cTrack, &msfStart))
  999. {
  1000. if (msfStart <= msfPos)
  1001. msfRel = msfPos - msfStart;
  1002. else
  1003. msfRel = 0;
  1004. *disktime = REDTH(msfPos, cTrack);
  1005. *tracktime = REDTH(msfRel, cTrack);
  1006. return TRUE;
  1007. }
  1008. else
  1009. {
  1010. continue;
  1011. }
  1012. }
  1013. else
  1014. {
  1015. dprintf4(("CdPosition - MSF disk pos: %u, %u, %u",
  1016. REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1017. *disktime = REDTH(msfPos, cTrack);
  1018. // data is current position
  1019. msfPos = MAKERED(sqd.CurrentPosition.TrackRelativeAddress[1],
  1020. sqd.CurrentPosition.TrackRelativeAddress[2],
  1021. sqd.CurrentPosition.TrackRelativeAddress[3]);
  1022. dprintf4(("CdPosition - MSF track pos (t,m,s,f): %u, %u, %u, %u",
  1023. cTrack, REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1024. *tracktime = REDTH(msfPos, cTrack);
  1025. return TRUE;
  1026. }
  1027. }
  1028. }
  1029. dprintf1(("CdPosition - Failed to read cd position"));
  1030. return FALSE;
  1031. }
  1032. /***************************************************************************
  1033. @doc EXTERNAL
  1034. @api MSF | CdDiskEnd | Get the position of the end of the disk.
  1035. @parm HCD | hCD | The handle of a currently open drive.
  1036. @rdesc The return value is the end position expressed
  1037. in MSF or INVALID_TRACK if an error occurs.
  1038. ***************************************************************************/
  1039. MSF CdDiskEnd(HCD hCD)
  1040. {
  1041. LPCDINFO lpInfo;
  1042. lpInfo = (LPCDINFO) hCD;
  1043. return lpInfo->msfEnd;
  1044. }
  1045. /***************************************************************************
  1046. @doc EXTERNAL
  1047. @api MSF | CdDiskLength | Get the length of the disk.
  1048. @parm HCD | hCD | The handle of a currently open drive.
  1049. @rdesc The return value is the length expressed
  1050. in MSF or INVALID_TRACK if an error occurs.
  1051. ***************************************************************************/
  1052. MSF CdDiskLength(HCD hCD)
  1053. {
  1054. LPCDINFO lpInfo;
  1055. MSF FirstTrackStart;
  1056. lpInfo = (LPCDINFO) hCD;
  1057. FirstTrackStart = CdTrackStart(hCD, lpInfo->FirstTrack);
  1058. if (FirstTrackStart == INVALID_TRACK) {
  1059. return INVALID_TRACK;
  1060. } else {
  1061. return reddiff(lpInfo->msfEnd, FirstTrackStart);
  1062. }
  1063. }
  1064. /***************************************************************************
  1065. @doc EXTERNAL
  1066. @api DWORD | CdStatus | Get the disk status.
  1067. @parm HCD | hCD | The handle of a currently open drive.
  1068. @rdesc The return value is the current audio status.
  1069. ***************************************************************************/
  1070. DWORD CdStatus(HCD hCD)
  1071. {
  1072. LPCDINFO lpInfo;
  1073. SUB_Q_CHANNEL_DATA sqd;
  1074. CDROM_SUB_Q_DATA_FORMAT Format;
  1075. DWORD CheckStatus;
  1076. DWORD ReadStatus;
  1077. UCHAR fStatus;
  1078. lpInfo = (LPCDINFO) hCD;
  1079. dprintf2(("CdStatus(%08XH)", hCD));
  1080. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  1081. FillMemory((PVOID)&sqd, sizeof(sqd), 0xFF);
  1082. //
  1083. // Check the disk status as well because IOCTL_CDROM_READ_Q_CHANNEL
  1084. // can return ERROR_SUCCESS even if there's no disk (I don't know why - or
  1085. // whether it's software bug in NT, hardware bug or valid!).
  1086. //
  1087. CheckStatus = cdIoctl(lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1088. if (ERROR_SUCCESS != CheckStatus) {
  1089. return DISC_NOT_READY;
  1090. }
  1091. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1092. &Format, sizeof(Format),
  1093. &sqd, sizeof(sqd));
  1094. if (ReadStatus == ERROR_NOT_READY) {
  1095. if (ERROR_SUCCESS == cdGetDiskInfo(lpInfo)) {
  1096. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1097. &Format, sizeof(Format),
  1098. &sqd, sizeof(sqd));
  1099. }
  1100. }
  1101. if (ERROR_SUCCESS != ReadStatus) {
  1102. //
  1103. // The read Q channel command is optional
  1104. //
  1105. dprintf1(("CdStatus - Failed to get Q channel data"));
  1106. return DISC_NOT_READY;
  1107. }
  1108. // Save previous audio status to prevent bug
  1109. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  1110. CdSetAudioStatus (hCD, fStatus);
  1111. dprintf4(("CdStatus - Status %02XH", fStatus));
  1112. switch (fStatus)
  1113. {
  1114. case AUDIO_STATUS_IN_PROGRESS:
  1115. dprintf4(("CdStatus - Playing"));
  1116. return DISC_PLAYING;
  1117. case AUDIO_STATUS_PAUSED:
  1118. dprintf4(("CdStatus - Paused"));
  1119. return DISC_PAUSED;
  1120. case AUDIO_STATUS_PLAY_COMPLETE:
  1121. dprintf4(("CdStatus - Stopped"));
  1122. return DISC_READY;
  1123. case AUDIO_STATUS_NO_STATUS:
  1124. // Grab previous status instead
  1125. switch (lpInfo->fPrevStatus)
  1126. {
  1127. #if 0
  1128. // NOTE: Be very careful before uncommenting the
  1129. // following 3 lines, they basically cause
  1130. // Play & wait to spin forever breaking
  1131. // "Continous Play", "Random Order" in CDPLAYER
  1132. // and MCI Play & wait commands.
  1133. // Basically, I didn't have time to track down
  1134. // the real problem. Apparently, the driver
  1135. // Is not returning AUDIO_STATUS_PLAY_COMPLETE
  1136. // when the CD reaches the end of the current
  1137. // play command. From my end, MCICDA is not
  1138. // receiving this. ChuckP says the lowlevel
  1139. // driver is generating this status correctly.
  1140. // Which I have verified
  1141. // So, the question is how is it getting lost?
  1142. case AUDIO_STATUS_IN_PROGRESS:
  1143. dprintf4(("CdStatus - Playing (Prev)"));
  1144. return DISC_PLAYING;
  1145. #endif
  1146. case AUDIO_STATUS_PAUSED:
  1147. dprintf4(("CdStatus - Paused (Prev)"));
  1148. return DISC_PAUSED;
  1149. case AUDIO_STATUS_PLAY_COMPLETE:
  1150. dprintf4(("CdStatus - Stopped (Prev)"));
  1151. return DISC_READY;
  1152. case AUDIO_STATUS_NO_STATUS:
  1153. default:
  1154. break;
  1155. } // End switch
  1156. dprintf4(("CdStatus - No status, assume stopped (Prev)"));
  1157. return DISC_READY;
  1158. //
  1159. // Some drives just return 0 sometimes - so we rely on the results of
  1160. // CHECK_VERIFY in this case
  1161. //
  1162. default:
  1163. dprintf4(("CdStatus - No status, assume Stopped"));
  1164. return DISC_READY;
  1165. }
  1166. } // End CdStatus
  1167. /***************************************************************************
  1168. @doc EXTERNAL
  1169. @api BOOL | CdEject | Eject the disk
  1170. @parm HCD | hCD | The handle of a currently open drive.
  1171. @rdesc The return value is the current status.
  1172. ***************************************************************************/
  1173. BOOL CdEject(HCD hCD)
  1174. {
  1175. LPCDINFO lpInfo;
  1176. BOOL fResult;
  1177. lpInfo = (LPCDINFO) hCD;
  1178. dprintf2(("CdEject(%08XH)", hCD));
  1179. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_EJECT_MEDIA, NULL, 0));
  1180. //if (fResult) {
  1181. // Save Audio Status to prevent bug
  1182. //lpInfo->fPrevStatus = 0;
  1183. //}
  1184. return fResult;
  1185. }
  1186. /***************************************************************************
  1187. @doc EXTERNAL
  1188. @api BOOL | CdPause | Pause the playing
  1189. @parm HCD | hCD | The handle of a currently open drive.
  1190. @rdesc The return value is the current status.
  1191. ***************************************************************************/
  1192. BOOL CdPause(HCD hCD)
  1193. {
  1194. LPCDINFO lpInfo;
  1195. lpInfo = (LPCDINFO) hCD;
  1196. dprintf2(("CdPause(%08XH)", hCD));
  1197. cdIoctl(lpInfo, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0);
  1198. //
  1199. // Ignore the return - we may have been paused or stopped already
  1200. //
  1201. return TRUE;
  1202. }
  1203. /***************************************************************************
  1204. @doc EXTERNAL
  1205. @api BOOL | CdResume | Resume the playing
  1206. @parm HCD | hCD | The handle of a currently open drive.
  1207. @rdesc The return value is the current status.
  1208. ***************************************************************************/
  1209. BOOL CdResume(HCD hCD)
  1210. {
  1211. LPCDINFO lpInfo;
  1212. lpInfo = (LPCDINFO) hCD;
  1213. dprintf2(("CdResume(%08XH)", hCD));
  1214. return ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_RESUME_AUDIO, NULL, 0);
  1215. }
  1216. /***************************************************************************
  1217. @doc EXTERNAL
  1218. @api BOOL | CdStop | Stop playing
  1219. @parm HCD | hCD | The handle of a currently open drive.
  1220. @rdesc The return value is the current status. Note that not
  1221. all devices support stop
  1222. ***************************************************************************/
  1223. BOOL CdStop(HCD hCD)
  1224. {
  1225. LPCDINFO lpInfo;
  1226. BOOL fResult;
  1227. lpInfo = (LPCDINFO) hCD;
  1228. dprintf2(("CdStop(%08XH)", hCD));
  1229. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_STOP_AUDIO, NULL, 0));
  1230. if (fResult)
  1231. {
  1232. lpInfo->fPrevStatus = AUDIO_STATUS_PLAY_COMPLETE;
  1233. }
  1234. return fResult;
  1235. }
  1236. /***************************************************************************
  1237. @doc EXTERNAL
  1238. @api BOOL | CdSetVolumeAll | Set the playing volume for all channels
  1239. @parm HCD | hCD | The handle of a currently open drive.
  1240. @parm UCHAR | Volume | The volume to set (FF = max)
  1241. @rdesc The return value is the current status.
  1242. ***************************************************************************/
  1243. BOOL CdSetVolumeAll(HCD hCD, UCHAR Volume)
  1244. {
  1245. LPCDINFO lpInfo;
  1246. VOLUME_CONTROL VolumeControl;
  1247. DWORD dwErr;
  1248. lpInfo = (LPCDINFO) hCD;
  1249. dprintf4(("CdSetVolumeAll(%08XH), Volume %u", hCD, Volume));
  1250. //
  1251. // Read the old values
  1252. //
  1253. dwErr = cdIoctl(lpInfo,
  1254. IOCTL_CDROM_GET_VOLUME,
  1255. (PVOID)&VolumeControl,
  1256. sizeof(VolumeControl));
  1257. if (dwErr != ERROR_SUCCESS)
  1258. {
  1259. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Error = %lx",
  1260. hCD, Volume, dwErr));
  1261. return FALSE;
  1262. }
  1263. // Set all channels to new volume
  1264. VolumeControl.PortVolume[0] = Volume;
  1265. VolumeControl.PortVolume[1] = Volume;
  1266. VolumeControl.PortVolume[2] = Volume;
  1267. VolumeControl.PortVolume[3] = Volume;
  1268. // Save what we think it should be
  1269. lpInfo->VolChannels[0] = Volume;
  1270. lpInfo->VolChannels[1] = Volume;
  1271. lpInfo->VolChannels[2] = Volume;
  1272. lpInfo->VolChannels[3] = Volume;
  1273. //
  1274. // Not all CDs support volume setting so don't check the return code here
  1275. //
  1276. dwErr = cdIoctl(lpInfo, IOCTL_CDROM_SET_VOLUME,
  1277. (PVOID)&VolumeControl,
  1278. sizeof(VolumeControl));
  1279. if (dwErr != ERROR_SUCCESS)
  1280. {
  1281. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Set Volume Failed (%08XH)",
  1282. hCD, Volume, dwErr));
  1283. }
  1284. #ifdef DEBUG
  1285. // Double Check
  1286. if (ERROR_SUCCESS != cdIoctl(lpInfo,
  1287. IOCTL_CDROM_GET_VOLUME,
  1288. (PVOID)&VolumeControl,
  1289. sizeof(VolumeControl)))
  1290. {
  1291. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Get Volume Failed (%08XH)",
  1292. hCD, Volume, dwErr));
  1293. }
  1294. // Compare actual to what we think it should be
  1295. if ((VolumeControl.PortVolume[0] != lpInfo->VolChannels[0]) ||
  1296. (VolumeControl.PortVolume[1] != lpInfo->VolChannels[1]) ||
  1297. (VolumeControl.PortVolume[2] != lpInfo->VolChannels[2]) ||
  1298. (VolumeControl.PortVolume[3] != lpInfo->VolChannels[3]))
  1299. {
  1300. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Channels don't match [%lx,%lx,%lx,%lx] != [%lx,%lx,%lx,%lx]",
  1301. hCD, Volume,
  1302. (DWORD)VolumeControl.PortVolume[0],
  1303. (DWORD)VolumeControl.PortVolume[1],
  1304. (DWORD)VolumeControl.PortVolume[2],
  1305. (DWORD)VolumeControl.PortVolume[3],
  1306. (DWORD)lpInfo->VolChannels[0],
  1307. (DWORD)lpInfo->VolChannels[1],
  1308. (DWORD)lpInfo->VolChannels[2],
  1309. (DWORD)lpInfo->VolChannels[3]
  1310. ));
  1311. }
  1312. #endif
  1313. return TRUE;
  1314. }
  1315. /***************************************************************************
  1316. @doc EXTERNAL
  1317. @api BOOL | CdSetVolume | Set the playing volume for one channel
  1318. @parm HCD | hCD | The handle of a currently open drive.
  1319. @parm int | Channel | The channel to set
  1320. @parm UCHAR | Volume | The volume to set (FF = max)
  1321. @rdesc The return value is the current status.
  1322. ***************************************************************************/
  1323. BOOL CdSetVolume(HCD hCD, int Channel, UCHAR Volume)
  1324. {
  1325. LPCDINFO lpInfo;
  1326. VOLUME_CONTROL VolumeControl;
  1327. DWORD dwErr;
  1328. lpInfo = (LPCDINFO) hCD;
  1329. dprintf4(("CdSetVolume(%08XH), Channel %d Volume %u", hCD, Channel, Volume));
  1330. VolumeControl.PortVolume[0] = lpInfo->VolChannels[0];
  1331. VolumeControl.PortVolume[1] = lpInfo->VolChannels[1];
  1332. VolumeControl.PortVolume[2] = lpInfo->VolChannels[2];
  1333. VolumeControl.PortVolume[3] = lpInfo->VolChannels[3];
  1334. //
  1335. // Read the old values
  1336. //
  1337. dwErr = cdIoctl(lpInfo,
  1338. IOCTL_CDROM_GET_VOLUME,
  1339. (PVOID)&VolumeControl,
  1340. sizeof(VolumeControl));
  1341. if (dwErr != ERROR_SUCCESS)
  1342. {
  1343. dprintf2(("CdSetVolume(%08XH), Channel %u, Volume %u, Error = %lx",
  1344. hCD, Channel, Volume, dwErr));
  1345. return FALSE;
  1346. }
  1347. // Check if it is already the correct value
  1348. if (VolumeControl.PortVolume[Channel] == Volume)
  1349. {
  1350. // Nothing to do
  1351. dprintf2(("CdSetVolume(%08XH), Channel %u, Volume %u, Already this volume!!!",
  1352. hCD, Channel, Volume));
  1353. return TRUE;
  1354. }
  1355. lpInfo->VolChannels[Channel] = Volume;
  1356. VolumeControl.PortVolume[Channel] = Volume;
  1357. //
  1358. // Not all CDs support volume setting so don't check the return code here
  1359. //
  1360. dwErr = cdIoctl(lpInfo, IOCTL_CDROM_SET_VOLUME,
  1361. (PVOID)&VolumeControl,
  1362. sizeof(VolumeControl));
  1363. if (dwErr != ERROR_SUCCESS)
  1364. {
  1365. dprintf2(("CdSetVolume(%08XH), Channel %d, Volume %u, Set Volume Failed (%08XH)",
  1366. hCD, Channel, Volume, dwErr));
  1367. }
  1368. #ifdef DEBUG
  1369. //
  1370. // Double Check our results
  1371. //
  1372. if (ERROR_SUCCESS != cdIoctl(lpInfo,
  1373. IOCTL_CDROM_GET_VOLUME,
  1374. (PVOID)&VolumeControl,
  1375. sizeof(VolumeControl)))
  1376. {
  1377. dprintf2(("CdSetVolumeAll(%08XH), Volume %u, Get Volume Failed (%08XH)",
  1378. hCD, Volume, dwErr));
  1379. }
  1380. // Compare actual to what we think it should be
  1381. if ((VolumeControl.PortVolume[0] != lpInfo->VolChannels[0]) ||
  1382. (VolumeControl.PortVolume[1] != lpInfo->VolChannels[1]) ||
  1383. (VolumeControl.PortVolume[2] != lpInfo->VolChannels[2]) ||
  1384. (VolumeControl.PortVolume[3] != lpInfo->VolChannels[3]))
  1385. {
  1386. dprintf2(("CdSetVolume (%08XH), Channel %u, Volume %u, Channels don't match [%lx,%lx,%lx,%lx] != [%lx,%lx,%lx,%lx]",
  1387. hCD, Channel, Volume,
  1388. (DWORD)VolumeControl.PortVolume[0],
  1389. (DWORD)VolumeControl.PortVolume[1],
  1390. (DWORD)VolumeControl.PortVolume[2],
  1391. (DWORD)VolumeControl.PortVolume[3],
  1392. (DWORD)lpInfo->VolChannels[0],
  1393. (DWORD)lpInfo->VolChannels[1],
  1394. (DWORD)lpInfo->VolChannels[2],
  1395. (DWORD)lpInfo->VolChannels[3]
  1396. ));
  1397. }
  1398. #endif
  1399. return TRUE;
  1400. }
  1401. /***************************************************************************
  1402. @doc EXTERNAL
  1403. @api BOOL | CdCloseTray | Close the tray
  1404. @parm HCD | hCD | The handle of a currently open drive.
  1405. @rdesc The return value is the current status.
  1406. ***************************************************************************/
  1407. BOOL CdCloseTray(HCD hCD)
  1408. {
  1409. LPCDINFO lpInfo;
  1410. BOOL fResult;
  1411. lpInfo = (LPCDINFO) hCD;
  1412. dprintf2(("CdCloseTray(%08XH)", hCD));
  1413. fResult = (ERROR_SUCCESS == cdIoctl(lpInfo, IOCTL_CDROM_LOAD_MEDIA, NULL, 0));
  1414. //if (fResult) {
  1415. // Save Audio Status to prevent bug
  1416. // lpInfo->fPrevStatus = 0;
  1417. //}
  1418. return fResult;
  1419. }
  1420. /***************************************************************************
  1421. @doc EXTERNAL
  1422. @api BOOL | CdNumTracks | Return the number of tracks and check
  1423. ready (updating TOC) as a side-effect.
  1424. @parm HCD | hCD | The handle of a currently open drive.
  1425. @rdesc The return value is the current status.
  1426. ***************************************************************************/
  1427. int CdNumTracks(HCD hCD)
  1428. {
  1429. LPCDINFO lpInfo;
  1430. DWORD Status;
  1431. MSF Position;
  1432. MSF Temp;
  1433. lpInfo = (LPCDINFO) hCD;
  1434. dprintf2(("CdNumTracks(%08XH)", hCD));
  1435. //
  1436. // If the driver does NOT cache the table of contents then we may
  1437. // fail if a play is in progress
  1438. //
  1439. // However, if we don't have a valid TOC to work with then we'll just
  1440. // have to blow away the play anyway.
  1441. //
  1442. if (!lpInfo->bTOCValid) {
  1443. Status = cdGetDiskInfo(lpInfo);
  1444. if (ERROR_SUCCESS != Status) {
  1445. //
  1446. // See if we failed because it's playing
  1447. //
  1448. if (Status == ERROR_BUSY) {
  1449. if (!lpInfo->bTOCValid) {
  1450. int i;
  1451. //
  1452. // Stop it one way or another
  1453. // Note that pause is no good because in a paused
  1454. // state we may still not be able to read the TOC
  1455. //
  1456. if (!CdPosition(hCD, &Temp, &Position)) {
  1457. CdStop(hCD);
  1458. } else {
  1459. //
  1460. // Can't call CdPlay because this needs a valid
  1461. // position!
  1462. //
  1463. CDROM_PLAY_AUDIO_MSF msfPlay;
  1464. //
  1465. // Set up the data for the call to the driver
  1466. //
  1467. msfPlay.StartingM = REDMINUTE(Position);
  1468. msfPlay.StartingS = REDSECOND(Position);
  1469. msfPlay.StartingF = REDFRAME(Position);
  1470. msfPlay.EndingM = REDMINUTE(Position);
  1471. msfPlay.EndingS = REDSECOND(Position);
  1472. msfPlay.EndingF = REDFRAME(Position);
  1473. cdIoctl(lpInfo, IOCTL_CDROM_PLAY_AUDIO_MSF, &msfPlay,
  1474. sizeof(msfPlay));
  1475. }
  1476. //
  1477. // Make sure the driver knows it's stopped and
  1478. // give it a chance to stop.
  1479. // (NOTE - Sony drive can take around 70ms)
  1480. //
  1481. for (i = 0; i < 60; i++) {
  1482. if (CdStatus(hCD) == DISC_PLAYING) {
  1483. Sleep(10);
  1484. } else {
  1485. break;
  1486. }
  1487. }
  1488. dprintf2(("Took %d tries to stop it!", i));
  1489. //
  1490. // Have another go
  1491. //
  1492. if (ERROR_SUCCESS != cdGetDiskInfo(lpInfo)) {
  1493. return 0;
  1494. }
  1495. }
  1496. } else {
  1497. return 0;
  1498. }
  1499. }
  1500. }
  1501. return lpInfo->LastTrack - lpInfo->FirstTrack + 1;
  1502. }
  1503. /***************************************************************************
  1504. @doc EXTERNAL
  1505. @api DWORD | CdDiskID | Return the disk id
  1506. @parm HCD | hCD | The handle of a currently open drive.
  1507. @rdesc The return value is the disk id or -1 if it can't be found.
  1508. ***************************************************************************/
  1509. DWORD CdDiskID(HCD hCD)
  1510. {
  1511. LPCDINFO lpInfo;
  1512. UINT i;
  1513. DWORD id;
  1514. dprintf2(("CdDiskID"));
  1515. lpInfo = (LPCDINFO) hCD;
  1516. if (!lpInfo->bTOCValid) {
  1517. if (ERROR_SUCCESS != cdGetDiskInfo(lpInfo))
  1518. {
  1519. dprintf2(("CdDiskID - Invalid TOC"));
  1520. return (DWORD)-1;
  1521. }
  1522. }
  1523. for (i = 0, id = 0;
  1524. i < (UINT)(lpInfo->LastTrack - lpInfo->FirstTrack + 1);
  1525. i++) {
  1526. id += lpInfo->Track[i].msfStart & 0x00FFFFFF;
  1527. }
  1528. if (lpInfo->LastTrack - lpInfo->FirstTrack + 1 <= 2) {
  1529. id += CDA_red2bin(reddiff(lpInfo->leadout, lpInfo->Track[0].msfStart));
  1530. }
  1531. return id;
  1532. }
  1533. /***************************************************************************
  1534. @doc EXTERNAL
  1535. @api BOOL | CdDiskUPC | Return the disk UPC code
  1536. @parm HCD | hCD | The handle of a currently open drive.
  1537. @parm LPTSTR | upc | Where to put the upc
  1538. @rdesc TRUE or FALSE if failed
  1539. ***************************************************************************/
  1540. BOOL CdDiskUPC(HCD hCD, LPTSTR upc)
  1541. {
  1542. LPCDINFO lpInfo;
  1543. SUB_Q_CHANNEL_DATA sqd;
  1544. CDROM_SUB_Q_DATA_FORMAT Format;
  1545. DWORD Status;
  1546. UINT i;
  1547. dprintf2(("CdDiskUPC"));
  1548. Format.Format = IOCTL_CDROM_MEDIA_CATALOG;
  1549. Format.Track = 0;
  1550. lpInfo = (LPCDINFO) hCD;
  1551. Status = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1552. &Format, sizeof(Format),
  1553. &sqd, sizeof(SUB_Q_MEDIA_CATALOG_NUMBER));
  1554. if (ERROR_SUCCESS != Status) {
  1555. return FALSE;
  1556. }
  1557. // Save previous audio status to prevent bug
  1558. CdSetAudioStatus (hCD, sqd.CurrentPosition.Header.AudioStatus);
  1559. //
  1560. // See if there's anything there
  1561. //
  1562. if (!sqd.MediaCatalog.Mcval ||
  1563. sqd.MediaCatalog.FormatCode != IOCTL_CDROM_MEDIA_CATALOG) {
  1564. return FALSE;
  1565. }
  1566. //
  1567. // Check the upc format :
  1568. //
  1569. // 1. ASCII at least 12 ASCII digits
  1570. // 2. packed bcd 6 packed BCD digits
  1571. // 3. unpacked upc
  1572. //
  1573. if (sqd.MediaCatalog.MediaCatalog[9] >= TEXT('0')) {
  1574. for (i = 0; i < 12; i++) {
  1575. if (sqd.MediaCatalog.MediaCatalog[i] < TEXT('0') ||
  1576. sqd.MediaCatalog.MediaCatalog[i] > TEXT('9')) {
  1577. return FALSE;
  1578. }
  1579. }
  1580. wsprintf(upc, TEXT("%12.12hs"), sqd.MediaCatalog.MediaCatalog);
  1581. return TRUE;
  1582. }
  1583. //
  1584. // See if it's packed or unpacked.
  1585. //
  1586. for (i = 0; i < 6; i++) {
  1587. if (sqd.MediaCatalog.MediaCatalog[i] > 9) {
  1588. //
  1589. // Packed - unpack
  1590. //
  1591. for (i = 6; i > 0; i --) {
  1592. UCHAR uBCD;
  1593. uBCD = sqd.MediaCatalog.MediaCatalog[i - 1];
  1594. sqd.MediaCatalog.MediaCatalog[i * 2 - 2] = (UCHAR)(uBCD >> 4);
  1595. sqd.MediaCatalog.MediaCatalog[i * 2 - 1] = (UCHAR)(uBCD & 0x0F);
  1596. }
  1597. break;
  1598. }
  1599. }
  1600. //
  1601. // Check everything is in range
  1602. //
  1603. for (i = 0; i < 12; i++) {
  1604. if (sqd.MediaCatalog.MediaCatalog[i] > 9) {
  1605. return FALSE;
  1606. }
  1607. }
  1608. for (i = 0; i < 12; i++) {
  1609. if (sqd.MediaCatalog.MediaCatalog[i] != 0) {
  1610. //
  1611. // There is a real media catalog
  1612. //
  1613. for (i = 0 ; i < 12; i++) {
  1614. wsprintf(upc + i, TEXT("%01X"), sqd.MediaCatalog.MediaCatalog[i]);
  1615. }
  1616. return TRUE;
  1617. }
  1618. }
  1619. return FALSE;
  1620. }
  1621. /***************************************************************************
  1622. @doc EXTERNAL
  1623. @api BOOL | CdGetDrive | Return the drive id if matches one in our
  1624. list
  1625. @parm LPTSTR | lpstrDeviceName | Name of the device
  1626. @parm DID * | pdid | Where to put the id
  1627. @rdesc TRUE or FALSE if failed
  1628. @comm We allow both the device form and drive form eg:
  1629. f:
  1630. \\.\f:
  1631. ***************************************************************************/
  1632. BOOL CdGetDrive(LPCTSTR lpstrDeviceName, DID * pdid)
  1633. {
  1634. DID didSearch;
  1635. TCHAR szDeviceName[10];
  1636. TCHAR szDeviceNameOnly[10];
  1637. dprintf2(("CdGetDrive"));
  1638. for (didSearch = 0; didSearch < NumDrives; didSearch++) {
  1639. wsprintf(szDeviceNameOnly, TEXT("%c:"), lpstrDeviceName[0]);
  1640. wsprintf(szDeviceName, TEXT("%c:"), CdInfo[didSearch].cDrive);
  1641. if (lstrcmpi(szDeviceName, szDeviceNameOnly) == 0) {
  1642. *pdid = didSearch;
  1643. return TRUE;
  1644. }
  1645. wsprintf(szDeviceNameOnly, TEXT("\\\\.\\%c:"), lpstrDeviceName[0]);
  1646. wsprintf(szDeviceName, TEXT("\\\\.\\%c:"), CdInfo[didSearch].cDrive);
  1647. if (lstrcmpi(szDeviceName, szDeviceNameOnly) == 0) {
  1648. *pdid = didSearch;
  1649. return TRUE;
  1650. }
  1651. }
  1652. return FALSE;
  1653. }
  1654. /***************************************************************************
  1655. @doc EXTERNAL
  1656. @api DWORD | CdStatusTrackPos | Get the disk status,track,and pos.
  1657. @parm HCD | hCD | The handle of a currently open drive.
  1658. @parm DWORD * | pStatus | return status code here
  1659. @parm MSF * | pTrackTime | return Track Time here
  1660. @parm MSF * | pDiscTime | return Track Time here
  1661. @rdesc The return value is success/failure.
  1662. This function does an end run around to get past the MCI functionality
  1663. In other words it is a major HACK. The only compelling reason for
  1664. this function is that it reduces the number of IOCTL's that CDPLAYER
  1665. generates every 1/2 second from ~15 to 1 on average. Reducing system
  1666. traffic for SCSI drives by a substantial factor
  1667. ***************************************************************************/
  1668. BOOL CdStatusTrackPos (
  1669. HCD hCD,
  1670. DWORD * pStatus,
  1671. MSF * pTrackTime,
  1672. MSF * pDiscTime)
  1673. {
  1674. LPCDINFO lpInfo;
  1675. SUB_Q_CHANNEL_DATA sqd;
  1676. CDROM_SUB_Q_DATA_FORMAT Format;
  1677. DWORD CheckStatus;
  1678. DWORD ReadStatus;
  1679. MSF msfPos;
  1680. BOOL fTryAgain = TRUE;
  1681. UCHAR fStatus;
  1682. UCHAR fCode;
  1683. UCHAR cTrack;
  1684. // Check hCD
  1685. lpInfo = (LPCDINFO) hCD;
  1686. if (!lpInfo)
  1687. {
  1688. dprintf2(("CdStatusTrackPos(%08LX), invalid hCD", hCD));
  1689. return FALSE;
  1690. }
  1691. // Check parameters
  1692. if ((!pStatus) || (!pTrackTime) || (!pDiscTime))
  1693. {
  1694. dprintf2(("CdStatusTrackPos(%c), invalid parameters", (char)(lpInfo->cDrive)));
  1695. return FALSE;
  1696. }
  1697. dprintf2(("CdStatusTrackPos(%08LX, %c), Enter",
  1698. hCD, (char)(lpInfo->cDrive)));
  1699. lblTRYAGAIN:
  1700. *pStatus = DISC_NOT_READY;
  1701. *pTrackTime = REDTH(0, 1);
  1702. *pDiscTime = REDTH(0, 0);
  1703. Format.Format = IOCTL_CDROM_CURRENT_POSITION;
  1704. FillMemory((PVOID)&sqd, sizeof(sqd), 0xFF);
  1705. //
  1706. // Read position and status
  1707. //
  1708. ReadStatus = cdIoctlData(lpInfo, IOCTL_CDROM_READ_Q_CHANNEL,
  1709. &Format, sizeof(Format), &sqd, sizeof(sqd));
  1710. switch (ReadStatus)
  1711. {
  1712. case ERROR_NOT_READY:
  1713. // Don't give up yet
  1714. if (fTryAgain)
  1715. {
  1716. if (ERROR_SUCCESS == cdGetDiskInfo(lpInfo))
  1717. {
  1718. // Try one more time before admitting defeat
  1719. fTryAgain = FALSE;
  1720. goto lblTRYAGAIN;
  1721. }
  1722. }
  1723. // Give up !!!
  1724. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = ERROR_NOT_READY",
  1725. hCD, (char)(lpInfo->cDrive)));
  1726. return FALSE;
  1727. case STATUS_VERIFY_REQUIRED:
  1728. // Check if disk is still in drive
  1729. CheckStatus = cdIoctl (lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1730. switch (CheckStatus)
  1731. {
  1732. case ERROR_NO_MEDIA_IN_DRIVE:
  1733. case ERROR_UNRECOGNIZED_MEDIA:
  1734. case ERROR_NOT_READY:
  1735. default:
  1736. *pStatus = DISC_NOT_READY;
  1737. break;
  1738. case ERROR_SUCCESS:
  1739. *pStatus = DISC_READY;
  1740. break;
  1741. }
  1742. break;
  1743. case ERROR_INVALID_FUNCTION:
  1744. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = ERROR_INVALID_FUNCTION failed",
  1745. hCD, (char)(lpInfo->cDrive)));
  1746. *pTrackTime = REDTH(0, 1);
  1747. *pDiscTime = REDTH(0, 0);
  1748. CdGetAudioStatus (hCD, 0, pStatus);
  1749. return TRUE;
  1750. case ERROR_SUCCESS:
  1751. // Success, fall through
  1752. fStatus = sqd.CurrentPosition.Header.AudioStatus;
  1753. fCode = sqd.CurrentPosition.FormatCode;
  1754. cTrack = sqd.CurrentPosition.TrackNumber;
  1755. break;
  1756. default:
  1757. // Failed
  1758. dprintf2(("CdStatusTrackPos(%08LX, %c), ReadQChannel = unknown error (%08LX) failed",
  1759. hCD, (char)(lpInfo->cDrive), (DWORD)ReadStatus));
  1760. return FALSE;
  1761. }
  1762. // Save previous audio status to prevent bug
  1763. CdSetAudioStatus (hCD, fStatus);
  1764. // Translate status code
  1765. CdGetAudioStatus (hCD, fStatus, pStatus);
  1766. dprintf2(("CdStatusTrackPos - Status %02XH", fStatus));
  1767. //
  1768. // Get Position
  1769. //
  1770. // If the track > 100 (outside spec'ed range)
  1771. // OR track > last track number
  1772. // then display an error message
  1773. if ((fCode == 0x01) &&
  1774. ((100 < cTrack) ||
  1775. ((lpInfo->bTOCValid) &&
  1776. (lpInfo->LastTrack < cTrack))))
  1777. {
  1778. // Always display this message on checked builds.
  1779. // We need some feeling for how often this happens
  1780. // It should NEVER happen, but (at least for NEC
  1781. // drives) we see it after a seek to end
  1782. dprintf1(("CDIoctlData returned track==%d, retrying", cTrack));
  1783. if (fTryAgain)
  1784. {
  1785. // Try one more time
  1786. fTryAgain = FALSE;
  1787. goto lblTRYAGAIN;
  1788. }
  1789. }
  1790. dprintf4(("Status = %02X, Length[0] = %02X, Length[1] = %02X",
  1791. fStatus,
  1792. sqd.CurrentPosition.Header.DataLength[0],
  1793. sqd.CurrentPosition.Header.DataLength[1]));
  1794. dprintf4((" Format %02XH", fCode));
  1795. dprintf4((" Absolute Address %02X%02X%02X%02XH",
  1796. sqd.CurrentPosition.AbsoluteAddress[0],
  1797. sqd.CurrentPosition.AbsoluteAddress[1],
  1798. sqd.CurrentPosition.AbsoluteAddress[2],
  1799. sqd.CurrentPosition.AbsoluteAddress[3]));
  1800. dprintf4((" Relative Address %02X%02X%02X%02XH",
  1801. sqd.CurrentPosition.TrackRelativeAddress[0],
  1802. sqd.CurrentPosition.TrackRelativeAddress[1],
  1803. sqd.CurrentPosition.TrackRelativeAddress[2],
  1804. sqd.CurrentPosition.TrackRelativeAddress[3]));
  1805. if (fCode == 0x01)
  1806. {
  1807. // MSF format ?
  1808. // data is current position
  1809. msfPos = MAKERED(sqd.CurrentPosition.AbsoluteAddress[1],
  1810. sqd.CurrentPosition.AbsoluteAddress[2],
  1811. sqd.CurrentPosition.AbsoluteAddress[3]);
  1812. //
  1813. // If position is 0 (this seems to happen on the Toshiba) then
  1814. // we'll try again
  1815. //
  1816. if (msfPos == 0)
  1817. {
  1818. if (fTryAgain)
  1819. {
  1820. fTryAgain = FALSE;
  1821. goto lblTRYAGAIN;
  1822. }
  1823. dprintf3(("CdStatusTrackPos(%08LX, %c), Position = 0",
  1824. hCD, (char)(lpInfo->cDrive), (DWORD)ReadStatus));
  1825. return FALSE;
  1826. }
  1827. dprintf4(("CdStatusTrackPos - MSF disk pos: %u, %u, %u",
  1828. REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1829. *pDiscTime = REDTH(msfPos, cTrack);
  1830. // data is current position
  1831. msfPos = MAKERED(sqd.CurrentPosition.TrackRelativeAddress[1],
  1832. sqd.CurrentPosition.TrackRelativeAddress[2],
  1833. sqd.CurrentPosition.TrackRelativeAddress[3]);
  1834. dprintf4(("CdStatusTrackPos - MSF track pos (t,m,s,f): %u, %u, %u, %u",
  1835. cTrack, REDMINUTE(msfPos), REDSECOND(msfPos), REDFRAME(msfPos)));
  1836. *pTrackTime = REDTH(msfPos, cTrack);
  1837. return TRUE;
  1838. }
  1839. dprintf1(("CdStatusTrackPos - Failed to read cd position"));
  1840. return FALSE;
  1841. }
  1842. /***************************************************************************
  1843. @doc INTERNAL
  1844. @api BOOL | CdSetAudioStatus |
  1845. @rdesc TRUE or FALSE if failed
  1846. ***************************************************************************/
  1847. void CdSetAudioStatus (HCD hCD, UCHAR fStatus)
  1848. {
  1849. LPCDINFO lpInfo;
  1850. if (! hCD)
  1851. return;
  1852. lpInfo = (LPCDINFO)hCD;
  1853. // Save Old status
  1854. switch (fStatus)
  1855. {
  1856. case AUDIO_STATUS_NO_STATUS:
  1857. // Do nothing
  1858. break;
  1859. case AUDIO_STATUS_NOT_SUPPORTED:
  1860. case AUDIO_STATUS_IN_PROGRESS:
  1861. case AUDIO_STATUS_PAUSED:
  1862. case AUDIO_STATUS_PLAY_COMPLETE:
  1863. case AUDIO_STATUS_PLAY_ERROR:
  1864. default:
  1865. // Save previous state
  1866. lpInfo->fPrevStatus = fStatus;
  1867. break;
  1868. }
  1869. } // End CdSetAudioStatus
  1870. /***************************************************************************
  1871. @doc INTERNAL
  1872. @api BOOL | CdGetAudioStatus |
  1873. @rdesc TRUE or FALSE if failed
  1874. ***************************************************************************/
  1875. BOOL CdGetAudioStatus (HCD hCD, UCHAR fStatus, DWORD * pStatus)
  1876. {
  1877. LPCDINFO lpInfo;
  1878. DWORD CheckStatus;
  1879. if ((! hCD) || (!pStatus))
  1880. return FALSE;
  1881. lpInfo = (LPCDINFO)hCD;
  1882. // Get status code
  1883. switch (fStatus)
  1884. {
  1885. case AUDIO_STATUS_IN_PROGRESS:
  1886. *pStatus = DISC_PLAYING;
  1887. break;
  1888. case AUDIO_STATUS_PAUSED:
  1889. *pStatus = DISC_PAUSED;
  1890. break;
  1891. case AUDIO_STATUS_PLAY_COMPLETE:
  1892. *pStatus = DISC_READY;
  1893. break;
  1894. case AUDIO_STATUS_NO_STATUS:
  1895. // Grab previous status instead
  1896. switch (lpInfo->fPrevStatus)
  1897. {
  1898. #if 0
  1899. // NOTE: Be very careful before uncommenting
  1900. // The following 3 lines, they basically
  1901. // Cause Play & wait on IDE CD-ROMS to
  1902. // spin forever breaking "Continous Play",
  1903. // "Random Order" in CDPLAYER and
  1904. // MCI Play & wait commands.
  1905. // Basically, I didn't have time to track down
  1906. // the real problem. Apparently, the driver
  1907. // Is not returning AUDIO_STATUS_PLAY_COMPLETE
  1908. // when the CD reaches the end of the current
  1909. // play command. From my end, MCICDA is not
  1910. // receiving this. ChuckP says the lowlevel
  1911. // driver is generating this status correctly.
  1912. // Which I have verified
  1913. // So, the question is how is it getting lost?
  1914. case AUDIO_STATUS_IN_PROGRESS:
  1915. *pStatus = DISC_PLAYING;
  1916. break;
  1917. #endif
  1918. case AUDIO_STATUS_PAUSED:
  1919. *pStatus = DISC_PAUSED;
  1920. break;
  1921. case AUDIO_STATUS_PLAY_COMPLETE:
  1922. *pStatus = DISC_READY;
  1923. break;
  1924. default:
  1925. // We are in a broken state, so just
  1926. // assume we are stopped, it's the safest
  1927. *pStatus = DISC_READY;
  1928. break;
  1929. } // End switch
  1930. break;
  1931. //
  1932. // Some drives just return 0 sometimes - so we rely on the results of
  1933. // CHECK_VERIFY in this case
  1934. //
  1935. default:
  1936. // Check if disk is still in drive
  1937. CheckStatus = cdIoctl (lpInfo, IOCTL_CDROM_CHECK_VERIFY, NULL, 0);
  1938. if (ERROR_SUCCESS != CheckStatus)
  1939. {
  1940. *pStatus = DISC_NOT_READY;
  1941. }
  1942. else
  1943. {
  1944. *pStatus = DISC_READY;
  1945. }
  1946. break;
  1947. } // End Switch
  1948. // Success
  1949. return TRUE;
  1950. } // CdGetAudioStatus
  1951. /***************************************************************************
  1952. @doc EXTERNAL
  1953. @api MSF | CdGetTrack | Given a position to find the corresponding track
  1954. if any
  1955. @parm LPCDINFO | lpInfo | Pointer to CD info including track data.
  1956. @parm MSF | msfStart | Position to start looking.
  1957. @rdesc A new MSF to play from / seek to within an audio track or
  1958. the end of the CD if none was found.
  1959. ***************************************************************************/
  1960. BOOL CdGetTrack(
  1961. LPCDINFO lpInfo,
  1962. MSF msfPos,
  1963. UCHAR * pTrack,
  1964. MSF * pmsfStart)
  1965. {
  1966. UINT tracknum;
  1967. MSF lastaudio = lpInfo->msfEnd;
  1968. //
  1969. // Search for the track which ends after ours and is audio
  1970. //
  1971. for (tracknum = 0; ;tracknum++) {
  1972. //
  1973. // Note that some CDs return positions outside the playing range
  1974. // sometimes (notably 0) so msfStart may be less than the first
  1975. // track start
  1976. //
  1977. //
  1978. // If we're beyond the start of the track and before the start
  1979. // of the next track then this is the track we want.
  1980. //
  1981. // We assume we're always beyond the start of the first track
  1982. // and we check that if we're looking at the last track then
  1983. // we check we're before the end of the CD.
  1984. //
  1985. if ((msfPos >= lpInfo->Track[tracknum].msfStart || tracknum == 0)
  1986. &&
  1987. (tracknum + lpInfo->FirstTrack == lpInfo->LastTrack
  1988. ? msfPos <= lpInfo->msfEnd
  1989. : msfPos < lpInfo->Track[tracknum + 1].msfStart))
  1990. {
  1991. if (!(lpInfo->Track[tracknum].Ctrl & IS_DATA_TRACK))
  1992. {
  1993. *pTrack = lpInfo->Track[tracknum].TrackNumber;
  1994. *pmsfStart = lpInfo->Track[tracknum].msfStart;
  1995. return TRUE;
  1996. }
  1997. }
  1998. //
  1999. // Exhausted all tracks ?
  2000. //
  2001. if (tracknum + lpInfo->FirstTrack >= lpInfo->LastTrack)
  2002. {
  2003. return FALSE;
  2004. }
  2005. }
  2006. }