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.

1669 lines
41 KiB

  1. /*******************************Module*Header*********************************\
  2. * Module Name: mcicda.c
  3. *
  4. * Media Control Architecture Redbook CD Audio Driver
  5. *
  6. * Created: 4/25/90
  7. * Author: DLL (DavidLe)
  8. *
  9. * History:
  10. * DavidLe - Based on MCI Pioneer Videodisc Driver
  11. * MikeRo 12/90 - 1/91
  12. * RobinSp 10th March 1992 - Move to Windows NT
  13. *
  14. * Copyright (c) 1990-1999 Microsoft Corporation
  15. *
  16. \****************************************************************************/
  17. #include <windows.h>
  18. #include <mmsystem.h>
  19. #include <mmddk.h>
  20. #include "mcicda.h"
  21. #include "cda.h"
  22. #include "cdio.h"
  23. #define CHECK_MSF
  24. #define MCICDA_BAD_TIME 0xFFFFFFFF
  25. HANDLE hInstance;
  26. UINT_PTR wTimerID;
  27. int nWaitingDrives;
  28. DRIVEDATA DriveTable[MCIRBOOK_MAX_DRIVES];
  29. // MBR This
  30. void CALLBACK TimerProc (
  31. HWND hwnd,
  32. UINT uMessage,
  33. UINT uTimer,
  34. DWORD dwParam)
  35. {
  36. DID i;
  37. int wStatus;
  38. for (i = 0; i < MCIRBOOK_MAX_DRIVES; ++i) {
  39. EnterCrit( CdInfo[i].DeviceCritSec );
  40. if (DriveTable[i].bActiveTimer) {
  41. // MBR can other conditions beside successful completion of the
  42. // play cause the != DISC_PLAYING?
  43. if ((wStatus = CDA_drive_status (i)) != DISC_PLAYING)
  44. {
  45. if (--nWaitingDrives <= 0)
  46. KillTimer (NULL, uTimer);
  47. DriveTable[i].dwPlayTo = MCICDA_BAD_TIME;
  48. DriveTable[i].bActiveTimer = FALSE;
  49. switch (wStatus)
  50. {
  51. case DISC_PLAYING:
  52. case DISC_PAUSED:
  53. case DISC_READY:
  54. wStatus = MCI_NOTIFY_SUCCESSFUL;
  55. break;
  56. default:
  57. wStatus = MCI_NOTIFY_FAILURE;
  58. break;
  59. }
  60. mciDriverNotify (DriveTable[i].hCallback,
  61. DriveTable[i].wDeviceID, wStatus);
  62. }
  63. }
  64. LeaveCrit( CdInfo[i].DeviceCritSec );
  65. }
  66. }
  67. /*****************************************************************************
  68. @doc INTERNAL MCICDA
  69. @api UINT | notify | This function handles the notify
  70. for all mci commands.
  71. @parm DID | didDrive | Drive identifier
  72. @parm WORD | wDeviceID | Calling device ID
  73. @parm BOOL | wStartTimer | A boolean indicating that a timer is to be
  74. started
  75. @parm UINT | wFlag | The flag to be passed by mciDriverNotify
  76. @parm LPMCI_GENERIC_PARMS | lpParms | For direct callback
  77. *****************************************************************************/
  78. UINT
  79. notify ( DID didDrive,
  80. MCIDEVICEID wDeviceID,
  81. BOOL wStartTimer,
  82. UINT wFlag,
  83. LPMCI_GENERIC_PARMS lpParms)
  84. {
  85. if (DriveTable[didDrive].bActiveTimer)
  86. {
  87. mciDriverNotify (DriveTable[didDrive].hCallback, wDeviceID,
  88. MCI_NOTIFY_SUPERSEDED);
  89. if (--nWaitingDrives <= 0)
  90. KillTimer (NULL, wTimerID);
  91. DriveTable[didDrive].bActiveTimer = FALSE;
  92. }
  93. if (!wStartTimer)
  94. mciDriverNotify ((HWND)lpParms->dwCallback, wDeviceID,
  95. wFlag);
  96. else
  97. {
  98. if (!DriveTable[didDrive].bActiveTimer &&
  99. nWaitingDrives++ == 0)
  100. {
  101. // MBR every 1/10 of a sec. Should this be a parameter?
  102. wTimerID = SetTimer (NULL, 1, 100, (TIMERPROC)TimerProc);
  103. if (wTimerID == 0)
  104. return MCICDAERR_NO_TIMERS;
  105. }
  106. DriveTable[didDrive].wDeviceID = wDeviceID;
  107. DriveTable[didDrive].bActiveTimer = TRUE;
  108. DriveTable[didDrive].hCallback = (HANDLE)lpParms->dwCallback;
  109. }
  110. return 0;
  111. }
  112. /*****************************************************************************
  113. @doc INTERNAL MCICDA
  114. @api void | abort_notify |
  115. @parm PINSTDATA | pInst | application instance data
  116. @rdesc
  117. @comm
  118. *****************************************************************************/
  119. void abort_notify (PINSTDATA pInst)
  120. {
  121. DID didDrive = pInst->uDevice;
  122. if (DriveTable[didDrive].bActiveTimer)
  123. {
  124. mciDriverNotify (DriveTable[didDrive].hCallback,
  125. pInst->uMCIDeviceID,
  126. MCI_NOTIFY_ABORTED);
  127. // Kill timer if appropriate
  128. if (--nWaitingDrives == 0)
  129. KillTimer (NULL, wTimerID);
  130. DriveTable[didDrive].dwPlayTo = MCICDA_BAD_TIME;
  131. DriveTable[didDrive].bActiveTimer = FALSE;
  132. }
  133. }
  134. /*
  135. Return TRUE if the drive is in a playable state
  136. */
  137. UINT disc_ready (DID didDrive)
  138. {
  139. // The disk is ready if we can read its TOC (note the
  140. // kernel driver works out if the TOC really needs reading
  141. if (CDA_disc_ready(didDrive)) {
  142. if (CDA_num_tracks(didDrive)) {
  143. return TRUE;
  144. } else {
  145. CDA_reset_drive(didDrive);
  146. return FALSE;
  147. }
  148. } else
  149. return FALSE;
  150. }
  151. /*
  152. * @func redbook | flip3 | Put minute/second/frame values in different order
  153. *
  154. * @parm redbook | rbIn | Current position as track|minute|second|frame
  155. *
  156. * @rdesc (redbook)0|frame|second|minute
  157. */
  158. redbook flip3 (redbook rbIn)
  159. {
  160. return MAKERED(MCI_MSF_MINUTE(rbIn),
  161. MCI_MSF_SECOND(rbIn),
  162. MCI_MSF_FRAME(rbIn));
  163. }
  164. /*
  165. * @func redbook | flip4 | Put track/minute/second/frame values in different order
  166. *
  167. * @parm redbook | rbIn | Current position as track|minute|second|frame
  168. *
  169. * @rdesc (redbook)frame|second|minute|track
  170. */
  171. redbook flip4 (redbook rbIn)
  172. {
  173. redbook rbOut;
  174. LPSTR lpOut = (LPSTR)&rbOut,
  175. lpIn = (LPSTR)&rbIn;
  176. lpOut[0] = lpIn[3];
  177. lpOut[1] = lpIn[2];
  178. lpOut[2] = lpIn[1];
  179. lpOut[3] = lpIn[0];
  180. return rbOut;
  181. }
  182. // MBR Return the absolute redbook time of track sTrack, rbTime into track
  183. /*****************************************************************************
  184. @doc INTERNAL MCICDA
  185. @api redbook | track_time | Return the absolute redbook time of
  186. track sTrack, rbTime into track
  187. @parm DID | didDrive |
  188. @parm int | sTrack |
  189. @parm redbook | rbTime |
  190. @rdesc
  191. @comm
  192. *****************************************************************************/
  193. redbook track_time (DID didDrive, int sTrack, redbook rbTime)
  194. {
  195. redbook rbTemp;
  196. rbTemp = CDA_track_start (didDrive, sTrack);
  197. if (rbTemp == INVALID_TRACK)
  198. return rbTemp;
  199. return redadd (rbTime, rbTemp);
  200. }
  201. redbook miltored(DWORD dwMill)
  202. {
  203. unsigned char m, s, f;
  204. long r1, r2;
  205. r1 = dwMill % 60000;
  206. m = (unsigned char) ((dwMill - r1) / 60000);
  207. r2 = r1 % 1000;
  208. s = (unsigned char) ((r1 - r2) / 1000);
  209. f = (unsigned char) ((r2 * 75) / 1000);
  210. return MAKERED(m, s, f);
  211. }
  212. DWORD redtomil(redbook rbRed)
  213. {
  214. // Adding an extra one ms to prevent rounding errors at start
  215. return (DWORD)REDMINUTE(rbRed) * 60000 +
  216. (DWORD)REDSECOND(rbRed) * 1000 +
  217. ((DWORD)REDFRAME(rbRed) * 1000) / 75 +
  218. 1;
  219. }
  220. #ifdef AUDIOPHILE
  221. DWORD NEAR PASCAL mcSeek(
  222. PINSTDATA pInst,
  223. DWORD dwFlags,
  224. LPMCI_SEEK_PARMS lpSeek );
  225. DWORD NEAR PASCAL GetAudioPhileInfo(LPCTSTR lpCDAFileName)
  226. {
  227. OFSTRUCT of;
  228. RIFFCDA cda;
  229. HFILE hf;
  230. //
  231. // open the file and read the CDA info.
  232. //
  233. if ((hf = _lopen (lpCDAFileName)) == HFILE_ERROR)
  234. return 0;
  235. _lread(hf, &cda, sizeof(cda));
  236. _lclose(hf);
  237. if (cda.dwRIFF != RIFF_RIFF || cda.dwCDDA != RIFF_CDDA)
  238. {
  239. return 0;
  240. }
  241. return MCI_MAKE_TMSF(cda.wTrack,0,0,0);
  242. }
  243. #endif
  244. DWORD mcOpen (
  245. PINSTDATA pInst,
  246. DWORD dwFlags,
  247. LPMCI_OPEN_PARMS lpOpen)
  248. {
  249. DID didDrive = (DID)pInst->uDevice;
  250. DID didOld = (DID)pInst->uDevice;
  251. UCHAR Volume;
  252. DWORD dwTempVol;
  253. int nUseCount;
  254. /* Instance Initialization */
  255. pInst->dwTimeFormat = MCI_FORMAT_MSF;
  256. /* If an ELEMENT_ID is specified, this could be a drive letter */
  257. if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
  258. {
  259. if ((dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) == (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
  260. {
  261. dprintf2(("mcOpen, (%08lX), Flags not compatible", (DWORD)didDrive));
  262. return MCIERR_FLAGS_NOT_COMPATIBLE;
  263. }
  264. //
  265. // Find the device corresponding to this name
  266. //
  267. if (COMMAND_SUCCESSFUL !=
  268. CDA_get_drive(lpOpen->lpstrElementName, &didDrive))
  269. {
  270. dprintf2(("mcOpen, (%08lX), Failed to get corresponding device", (DWORD)didDrive));
  271. return MCIERR_INVALID_FILE;
  272. }
  273. dprintf2(("mcOpen, changing from drive (%08lx) to drive (%08lX)", (DWORD)(pInst->uDevice), (DWORD)didDrive));
  274. pInst->uDevice = didDrive;
  275. }
  276. /* Device Initialization */
  277. nUseCount = DriveTable[didDrive].nUseCount;
  278. if (nUseCount > 0)
  279. {
  280. // This drive is already open as another MCI device
  281. if (dwFlags & MCI_OPEN_SHAREABLE &&
  282. DriveTable[didDrive].bShareable)
  283. {
  284. // Shareable was specified so just increment the use count
  285. nUseCount++;
  286. dprintf2(("mcOpen, drive (%08lx), Incrementing UseCount, now = %ld",
  287. (DWORD)didDrive, (DWORD)nUseCount));
  288. }
  289. else
  290. {
  291. dprintf2(("mcOpen, drive (%08lx), tryed to share without specifing MCI_OPEN_SHAREABLE",
  292. (DWORD)didDrive));
  293. return MCIERR_MUST_USE_SHAREABLE;
  294. }
  295. }
  296. else
  297. {
  298. nUseCount = 1;
  299. }
  300. if (!CDA_open(didDrive))
  301. {
  302. dprintf2(("mcOpen, drive (%08lx), failed to open, UseCount = %ld",
  303. (DWORD)didDrive, (DWORD)nUseCount));
  304. return MCIERR_DEVICE_OPEN;
  305. }
  306. //
  307. // Don't call disc_ready here because it will read the table of
  308. // contents and on some drivers this will terminate any play
  309. // unnecessarily
  310. //
  311. if (CDA_drive_status (didDrive) == DISC_PLAYING)
  312. DriveTable[didDrive].bDiscPlayed = TRUE;
  313. else
  314. DriveTable[didDrive].bDiscPlayed = FALSE;
  315. DriveTable[didDrive].bActiveTimer = FALSE;
  316. DriveTable[didDrive].dwPlayTo = MCICDA_BAD_TIME;
  317. DriveTable[didDrive].bShareable = (dwFlags & MCI_OPEN_SHAREABLE) != 0;
  318. DriveTable[didDrive].nUseCount = nUseCount;
  319. dprintf2(("mcOpen, drive (%08lx), Setting UseCount = %ld",
  320. (DWORD)didDrive, (DWORD)nUseCount));
  321. //dstewart: fix for when vol in registry is > 8 bits
  322. dwTempVol = CDAudio_GetUnitVolume(didDrive);
  323. if (dwTempVol > 0xFF)
  324. {
  325. dwTempVol = 0xFF;
  326. }
  327. Volume = (UCHAR)dwTempVol;
  328. CDA_set_audio_volume_all (didDrive, Volume);
  329. #ifdef AUDIOPHILE
  330. /*
  331. * AudioPhile track information handler.
  332. *
  333. * The new CDROM file system for Windows 4.0 produces files that describe
  334. * CDAudio tracks. If a user wants to play a track, she should be able
  335. * to double click on the track. So, we add open element support here
  336. * and add an mplayer association s.t. the file may be read and the disc
  337. * played back. We need to reject the Phile if a CDROM of this ID can't
  338. * be found. A message box should be displayed if the disc is incorrect.
  339. * Repercussions of this feature are that we need to simulate a disc in
  340. * a data structure.
  341. */
  342. if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
  343. {
  344. MCI_SEEK_PARMS Seek;
  345. pInst->dwTimeFormat = MCI_FORMAT_TMSF;
  346. Seek.dwTo = GetAudioPhileInfo(lpOpen->lpstrElementName);
  347. if (Seek.dwTo != 0L)
  348. mcSeek(pInst, MCI_TO, (LPMCI_SEEK_PARMS)&Seek);
  349. }
  350. #endif
  351. return 0;
  352. }
  353. #define MSF_BITS ((redbook) 0x00FFFFFF)
  354. /*****************************************************************************
  355. @doc INTERNAL MCICDA
  356. @api redbook | convert_time | Take a DWORD time value and
  357. convert from current time format into redbook.
  358. @parm PINSTDATA | pInst | Pointer to application instance data
  359. @parm DWORD | dwTimeIn |
  360. @rdesc Return MCICDA_BAD_TIME if out of range.
  361. @comm
  362. *****************************************************************************/
  363. redbook convert_time(
  364. PINSTDATA pInst,
  365. DWORD dwTimeIn )
  366. {
  367. DID didDrive = (DID)pInst->uDevice;
  368. redbook rbTime;
  369. short nTrack;
  370. switch (pInst->dwTimeFormat)
  371. {
  372. case MCI_FORMAT_MILLISECONDS:
  373. rbTime = miltored (dwTimeIn);
  374. return rbTime;
  375. case MCI_FORMAT_MSF:
  376. dprintf3(("Time IN: %lu",dwTimeIn));
  377. rbTime = flip3 (dwTimeIn);
  378. dprintf3(("Time OUT: %d:%d:%d:%d", REDTRACK(rbTime), REDMINUTE(rbTime),REDSECOND(rbTime), REDFRAME(rbTime)));
  379. break;
  380. case MCI_FORMAT_TMSF:
  381. nTrack = (short)(dwTimeIn & 0xFF);
  382. if (nTrack > CDA_num_tracks( didDrive))
  383. return MCICDA_BAD_TIME;
  384. rbTime = track_time (didDrive, nTrack, flip3 (dwTimeIn >> 8));
  385. if (rbTime == INVALID_TRACK)
  386. return MCICDA_BAD_TIME;
  387. break;
  388. }
  389. #ifdef CHECK_MSF
  390. if ((REDFRAME(rbTime)>74) || (REDMINUTE(rbTime)>99) ||
  391. (REDSECOND(rbTime)>59))
  392. return MCICDA_BAD_TIME;
  393. #endif
  394. return rbTime;
  395. }
  396. /*****************************************************************************
  397. @doc INTERNAL MCICDA
  398. @api DWORD | seek | Process the MCI_SEEK command
  399. @parm PINSTDATA | pInst | Pointer to application instance data
  400. @parm DWORD | dwFlags |
  401. @parm LPMCI_SEEK_PARMS | lpSeek |
  402. @rdesc
  403. @comm
  404. *****************************************************************************/
  405. DWORD mcSeek(
  406. PINSTDATA pInst,
  407. DWORD dwFlags,
  408. LPMCI_SEEK_PARMS lpSeek )
  409. {
  410. DID didDrive = pInst->uDevice;
  411. redbook rbTime = 0;
  412. LPSTR lpTime = (LPSTR) &rbTime;
  413. redbook rbStart;
  414. redbook rbEnd;
  415. BOOL fForceAudio;
  416. dprintf3(("Seek, drive %d TO %8x", didDrive, lpSeek->dwTo));
  417. abort_notify (pInst);
  418. if ( !disc_ready (didDrive))
  419. return MCIERR_HARDWARE;
  420. if ((rbStart = CDA_track_start( didDrive, 1)) == INVALID_TRACK)
  421. return MCIERR_HARDWARE;
  422. rbStart &= MSF_BITS;
  423. if ((rbEnd = CDA_disc_end( didDrive)) == INVALID_TRACK)
  424. return MCIERR_HARDWARE;
  425. rbEnd &= MSF_BITS;
  426. // Check only one positioning command is given.
  427. // First isolate the bits we want
  428. // Then subtract 1. This removes the least significant bit, and puts
  429. // ones in any lower bit positions. Leaves other bits untouched.
  430. // If any bits are left on, more than one of TO, START or END was given
  431. // Note: if NO flags are given this ends up ANDING 0 with -1 == 0
  432. // which is OK.
  433. #define SEEK_BITS (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END))
  434. #define CHECK_FLAGS (((SEEK_BITS)-1) & (SEEK_BITS))
  435. if (CHECK_FLAGS) {
  436. return MCIERR_FLAGS_NOT_COMPATIBLE;
  437. }
  438. if (dwFlags & MCI_TO)
  439. {
  440. // When the above test is reviewed and proven to pick out
  441. // incompatible flags delete these lines.
  442. // Note: we detect more incompatible cases than Win 16 - this
  443. // is deliberate and fixes a Win 16 bug. CurtisP has seen this code.
  444. //if (dwFlags & (MCI_SEEK_TO_START | MCI_SEEK_TO_END))
  445. // return MCIERR_FLAGS_NOT_COMPATIBLE;
  446. if ((rbTime = convert_time (pInst, lpSeek->dwTo)) == MCICDA_BAD_TIME)
  447. return MCIERR_OUTOFRANGE;
  448. // if seek pos is before valid audio return an error
  449. if ( rbTime < rbStart)
  450. return MCIERR_OUTOFRANGE;
  451. // similarly, if seek pos is past end of disk return an error
  452. else if (rbTime > rbEnd)
  453. return MCIERR_OUTOFRANGE;
  454. fForceAudio = FALSE;
  455. } else if (dwFlags & MCI_SEEK_TO_START) {
  456. rbTime = rbStart;
  457. fForceAudio = TRUE; // We want the first audio track
  458. } else if (dwFlags & MCI_SEEK_TO_END) {
  459. rbTime = rbEnd;
  460. fForceAudio = TRUE; // We want the last audio track
  461. } else {
  462. return MCIERR_MISSING_PARAMETER;
  463. }
  464. // send seek command to driver
  465. if (CDA_seek_audio (didDrive, rbTime, fForceAudio) != COMMAND_SUCCESSFUL)
  466. return MCIERR_HARDWARE;
  467. if (CDA_pause_audio (didDrive) != COMMAND_SUCCESSFUL)
  468. return MCIERR_HARDWARE;
  469. DriveTable[didDrive].bDiscPlayed = TRUE;
  470. return 0;
  471. }
  472. /*****************************************************************************
  473. @doc INTERNAL MCICDA
  474. @api BOOL | wait |
  475. @parm DWORD | dwFlags |
  476. @parm PINSTDATA | pInst | Pointer to application instance data
  477. @rdesc Return TRUE if BREAK was pressed
  478. @comm If the wait flag is set then wait until the device is no longer playing
  479. *****************************************************************************/
  480. BOOL wait (
  481. DWORD dwFlags,
  482. PINSTDATA pInst )
  483. {
  484. DID didDrive = pInst->uDevice;
  485. MCIDEVICEID wDeviceID = pInst->uMCIDeviceID;
  486. if (dwFlags & MCI_WAIT)
  487. {
  488. //Note: jyg This is interesting. I've noticed that some drives do give
  489. // sporadic errors. Thus this retry stuff. 5X is enough to
  490. // determine true failure.
  491. int status, retry=0;
  492. retry:
  493. while ((status = CDA_drive_status (didDrive)) == DISC_PLAYING) {
  494. LeaveCrit( CdInfo[didDrive].DeviceCritSec );
  495. if (mciDriverYield (wDeviceID) != 0) {
  496. EnterCrit( CdInfo[didDrive].DeviceCritSec );
  497. return TRUE;
  498. }
  499. Sleep(50);
  500. EnterCrit( CdInfo[didDrive].DeviceCritSec );
  501. }
  502. if (status == DISC_NOT_READY && retry++ < 5)
  503. goto retry;
  504. }
  505. return FALSE;
  506. }
  507. /*****************************************************************************
  508. @doc INTERNAL MCICDA
  509. @api DWORD | play | Process the MCI_PLAY command
  510. @parm PINSTDATA | pInst | Pointer to application instance data
  511. @parm DWORD | dwFlags |
  512. @parm LPMCI_PLAY_PARMS | lpPlay |
  513. @parm BOOL FAR * | bBreak |
  514. @rdesc
  515. @comm
  516. *****************************************************************************/
  517. DWORD mcPlay(
  518. PINSTDATA pInst,
  519. DWORD dwFlags,
  520. LPMCI_PLAY_PARMS lpPlay,
  521. BOOL FAR * bBreak )
  522. {
  523. DID didDrive = pInst->uDevice;
  524. redbook rbFrom, rbTo;
  525. redbook dStart, dEnd;
  526. BOOL bAbort = FALSE;
  527. if (!disc_ready (didDrive)) // MBR could return more specific error
  528. return MCIERR_HARDWARE;
  529. // do we have both from and to parameters?
  530. // If so then do a "seek" instead
  531. if ((dwFlags & (MCI_FROM | MCI_TO)) == (MCI_FROM | MCI_TO))
  532. if (lpPlay->dwTo == lpPlay->dwFrom)
  533. // Convert a 'play x to x' into 'seek to x'
  534. {
  535. MCI_SEEK_PARMS Seek;
  536. Seek.dwTo = lpPlay->dwFrom;
  537. Seek.dwCallback = lpPlay->dwCallback;
  538. return mcSeek(pInst, dwFlags, (LPMCI_SEEK_PARMS)&Seek);
  539. }
  540. // mask is to ignore track number in the upper byte
  541. // which appears at some times
  542. dStart = CDA_track_start( didDrive, 1) & MSF_BITS;
  543. dEnd = CDA_disc_end( didDrive) & MSF_BITS;
  544. if (dwFlags & MCI_TO)
  545. {
  546. if ((rbTo = convert_time (pInst, lpPlay->dwTo))
  547. == MCICDA_BAD_TIME)
  548. return MCIERR_OUTOFRANGE;
  549. } else
  550. rbTo = dEnd;
  551. if (dwFlags & MCI_FROM)
  552. {
  553. if ((rbFrom = convert_time (pInst, lpPlay->dwFrom))
  554. == MCICDA_BAD_TIME)
  555. return MCIERR_OUTOFRANGE;
  556. } else // no FROM
  557. {
  558. // If the disk has never played the current position is indeterminate so
  559. // we must start from the beginning
  560. if (!DriveTable[didDrive].bDiscPlayed)
  561. {
  562. // Initial position is at the beginning of track 1
  563. rbFrom = track_time (didDrive, (int)1, (redbook)0);
  564. if (rbFrom == INVALID_TRACK)
  565. return MCIERR_HARDWARE;
  566. } else if ((!(dwFlags & MCI_TO) ||
  567. rbTo == DriveTable[didDrive].dwPlayTo) &&
  568. CDA_drive_status (didDrive) == DISC_PLAYING)
  569. // Disc is playing and no (or redundent) "to" position was
  570. // specified so do nothng
  571. goto exit_fn;
  572. else
  573. {
  574. CDA_time_info (didDrive, NULL, &rbFrom);
  575. // Current position in track 0 means play starting from track 1
  576. if (REDTRACK(rbFrom) == 0)
  577. {
  578. rbFrom = track_time (didDrive, (int)1, (redbook)0);
  579. if (rbFrom == INVALID_TRACK)
  580. return MCIERR_HARDWARE;
  581. }
  582. rbFrom &= MSF_BITS;
  583. // Some drives (SONY) will return an illegal position
  584. if (rbFrom < dStart)
  585. rbFrom = dStart;
  586. }
  587. }
  588. rbFrom &= MSF_BITS;
  589. rbTo &= MSF_BITS;
  590. if (dwFlags & MCI_TO)
  591. {
  592. if (rbFrom > rbTo || rbTo > dEnd)
  593. return MCIERR_OUTOFRANGE;
  594. } else {
  595. rbTo = dEnd;
  596. }
  597. // if From is before audio start return an error
  598. if ( rbFrom < dStart)
  599. return MCIERR_OUTOFRANGE;
  600. if (dwFlags & MCI_FROM) {
  601. // Try a seek - don't care if it works (!)
  602. CDA_seek_audio(didDrive, rbFrom, TRUE);
  603. }
  604. // send play command to driver
  605. if (CDA_play_audio(didDrive, rbFrom, rbTo)
  606. != COMMAND_SUCCESSFUL)
  607. return MCIERR_HARDWARE; // values should be vaild so err is hard
  608. DriveTable[didDrive].bDiscPlayed = TRUE;
  609. exit_fn:;
  610. // Abort if either from or (a new) to position is specified
  611. if (dwFlags & MCI_FROM || rbTo != DriveTable[didDrive].dwPlayTo)
  612. abort_notify (pInst);
  613. *bBreak = wait(dwFlags, pInst);
  614. DriveTable[didDrive].dwPlayTo = rbTo;
  615. return 0;
  616. }
  617. /*****************************************************************************
  618. @doc INTERNAL MCICDA
  619. @api DWORD | mcGetDevCaps | Process the MCI_GETDEVCAPS command
  620. @parm PINSTDATA | pInst | Pointer to application data instance
  621. @parm DWORD | dwFlags |
  622. @parm LPMCI_GETDEVCAPS_PARMS | lpCaps |
  623. @rdesc
  624. @comm
  625. *****************************************************************************/
  626. DWORD mcGetDevCaps(
  627. PINSTDATA pInst,
  628. DWORD dwFlags,
  629. LPMCI_GETDEVCAPS_PARMS lpCaps )
  630. {
  631. DWORD dwReturn = 0;
  632. if (!(dwFlags & MCI_GETDEVCAPS_ITEM))
  633. return MCIERR_MISSING_PARAMETER;
  634. switch (lpCaps->dwItem)
  635. {
  636. case MCI_GETDEVCAPS_CAN_RECORD:
  637. case MCI_GETDEVCAPS_CAN_SAVE:
  638. case MCI_GETDEVCAPS_HAS_VIDEO:
  639. case MCI_GETDEVCAPS_USES_FILES:
  640. case MCI_GETDEVCAPS_COMPOUND_DEVICE:
  641. lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  642. dwReturn = MCI_RESOURCE_RETURNED;
  643. break;
  644. case MCI_GETDEVCAPS_HAS_AUDIO:
  645. case MCI_GETDEVCAPS_CAN_EJECT: // mbr - bogus...
  646. case MCI_GETDEVCAPS_CAN_PLAY:
  647. lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  648. dwReturn = MCI_RESOURCE_RETURNED;
  649. break;
  650. case MCI_GETDEVCAPS_DEVICE_TYPE:
  651. lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO,
  652. MCI_DEVTYPE_CD_AUDIO);
  653. dwReturn = MCI_RESOURCE_RETURNED;
  654. break;
  655. default:
  656. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  657. break;
  658. }
  659. return dwReturn;
  660. }
  661. /*****************************************************************************
  662. @doc INTERNAL MCICDA
  663. @api DWORD | mcStatus | Process the MCI_STATUS command
  664. @parm PINSTDATA | pInst | Pointer to application instance data
  665. @parm DWORD | dwFlags |
  666. @parm LPMCI_STATUS_PARMS | lpStatus |
  667. @rdesc
  668. @comm
  669. *****************************************************************************/
  670. DWORD mcStatus (
  671. PINSTDATA pInst,
  672. DWORD dwFlags,
  673. LPMCI_STATUS_PARMS lpStatus)
  674. {
  675. DID didDrive = (DID)pInst->uDevice;
  676. DWORD dwReturn = 0;
  677. if (!(dwFlags & MCI_STATUS_ITEM))
  678. return MCIERR_MISSING_PARAMETER;
  679. switch (lpStatus->dwItem)
  680. {
  681. int n;
  682. case MCI_STATUS_MEDIA_PRESENT:
  683. if (CDA_traystate(didDrive) != TRAY_OPEN &&
  684. CDA_disc_ready(didDrive))
  685. lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  686. else
  687. lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  688. dwReturn = MCI_RESOURCE_RETURNED;
  689. break;
  690. case MCI_STATUS_READY:
  691. switch (CDA_drive_status (didDrive))
  692. {
  693. case DISC_PLAYING:
  694. case DISC_PAUSED:
  695. case DISC_READY:
  696. lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
  697. break;
  698. default:
  699. lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
  700. break;
  701. }
  702. dwReturn = MCI_RESOURCE_RETURNED;
  703. break;
  704. case MCI_STATUS_MODE:
  705. {
  706. switch (CDA_drive_status (didDrive))
  707. {
  708. case DISC_PLAYING:
  709. n = MCI_MODE_PLAY;
  710. break;
  711. case DISC_PAUSED:
  712. n = MCI_MODE_STOP; // HACK HACK!
  713. break;
  714. case DISC_READY:
  715. n = MCI_MODE_STOP;
  716. break;
  717. default:
  718. if (CDA_traystate (didDrive) == TRAY_OPEN)
  719. n = MCI_MODE_OPEN;
  720. else
  721. n = MCI_MODE_NOT_READY;
  722. break;
  723. }
  724. lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(n, n);
  725. dwReturn = MCI_RESOURCE_RETURNED;
  726. break;
  727. }
  728. case MCI_STATUS_TIME_FORMAT:
  729. n = (WORD)pInst->dwTimeFormat;
  730. lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(n,n + MCI_FORMAT_RETURN_BASE);
  731. dwReturn = MCI_RESOURCE_RETURNED;
  732. break;
  733. case MCI_STATUS_POSITION:
  734. {
  735. redbook tracktime, disctime;
  736. if (dwFlags & MCI_TRACK)
  737. {
  738. int n;
  739. if (dwFlags & MCI_STATUS_START)
  740. return MCIERR_FLAGS_NOT_COMPATIBLE;
  741. if (!disc_ready(didDrive))
  742. return MCIERR_HARDWARE;
  743. if ((n = CDA_num_tracks (didDrive)) == 0)
  744. return MCIERR_HARDWARE;
  745. if (!lpStatus->dwTrack || lpStatus->dwTrack > (DWORD)n)
  746. return MCIERR_OUTOFRANGE;
  747. lpStatus->dwReturn =
  748. CDA_track_start (didDrive, (short)lpStatus->dwTrack);
  749. switch (pInst->dwTimeFormat)
  750. {
  751. case MCI_FORMAT_MILLISECONDS:
  752. lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn);
  753. dwReturn = 0;
  754. break;
  755. case MCI_FORMAT_TMSF:
  756. lpStatus->dwReturn = lpStatus->dwTrack;
  757. dwReturn = MCI_COLONIZED4_RETURN;
  758. break;
  759. case MCI_FORMAT_MSF:
  760. lpStatus->dwReturn = flip3 ((redbook)lpStatus->dwReturn);
  761. dwReturn = MCI_COLONIZED3_RETURN;
  762. break;
  763. }
  764. } else if (dwFlags & MCI_STATUS_START)
  765. {
  766. if (!disc_ready(didDrive))
  767. return MCIERR_HARDWARE;
  768. if ((n = CDA_num_tracks (didDrive)) == 0)
  769. return MCIERR_HARDWARE;
  770. lpStatus->dwReturn =
  771. CDA_track_start (didDrive, 1);
  772. switch (pInst->dwTimeFormat)
  773. {
  774. case MCI_FORMAT_MILLISECONDS:
  775. lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn);
  776. dwReturn = 0;
  777. break;
  778. case MCI_FORMAT_TMSF:
  779. lpStatus->dwReturn = 1;
  780. dwReturn = MCI_COLONIZED4_RETURN;
  781. break;
  782. case MCI_FORMAT_MSF:
  783. lpStatus->dwReturn = flip3 ((redbook)lpStatus->dwReturn);
  784. dwReturn = MCI_COLONIZED3_RETURN;
  785. break;
  786. }
  787. } else
  788. {
  789. if (!DriveTable[didDrive].bDiscPlayed)
  790. {
  791. tracktime = REDTH(0, 1);
  792. if (!disc_ready(didDrive))
  793. return MCIERR_HARDWARE;
  794. disctime = CDA_track_start( didDrive, 1);
  795. } else if (CDA_time_info(didDrive, &tracktime, &disctime) !=
  796. COMMAND_SUCCESSFUL)
  797. return MCIERR_HARDWARE;
  798. if (REDTRACK(tracktime) == 0)
  799. {
  800. tracktime = (redbook)0;
  801. disctime = (redbook)0;
  802. }
  803. switch (pInst->dwTimeFormat)
  804. {
  805. case MCI_FORMAT_MILLISECONDS:
  806. lpStatus->dwReturn = redtomil (disctime);
  807. dwReturn = 0;
  808. break;
  809. case MCI_FORMAT_MSF:
  810. lpStatus->dwReturn = flip3(disctime);
  811. dwReturn = MCI_COLONIZED3_RETURN;
  812. break;
  813. case MCI_FORMAT_TMSF:
  814. lpStatus->dwReturn = flip4 (tracktime);
  815. dwReturn = MCI_COLONIZED4_RETURN;
  816. break;
  817. }
  818. }
  819. break;
  820. }
  821. case MCI_STATUS_LENGTH:
  822. if (!disc_ready(didDrive))
  823. return MCIERR_HARDWARE;
  824. if (dwFlags & MCI_TRACK)
  825. {
  826. lpStatus->dwReturn =
  827. CDA_track_length (didDrive, (short)lpStatus->dwTrack);
  828. if (lpStatus->dwReturn == INVALID_TRACK)
  829. return MCIERR_OUTOFRANGE;
  830. switch (pInst->dwTimeFormat)
  831. {
  832. case MCI_FORMAT_MILLISECONDS:
  833. lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn);
  834. dwReturn = 0;
  835. break;
  836. case MCI_FORMAT_MSF:
  837. case MCI_FORMAT_TMSF:
  838. lpStatus->dwReturn = flip3((redbook)lpStatus->dwReturn);
  839. dwReturn = MCI_COLONIZED3_RETURN;
  840. break;
  841. }
  842. } else
  843. {
  844. // Subtract one to match SEEK_TO_END
  845. lpStatus->dwReturn = CDA_disc_length (didDrive);
  846. switch (pInst->dwTimeFormat)
  847. {
  848. case MCI_FORMAT_MILLISECONDS:
  849. lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn);
  850. dwReturn = 0;
  851. break;
  852. case MCI_FORMAT_MSF:
  853. case MCI_FORMAT_TMSF:
  854. lpStatus->dwReturn = flip3((redbook)lpStatus->dwReturn);
  855. dwReturn = MCI_COLONIZED3_RETURN;
  856. break;
  857. }
  858. }
  859. break;
  860. case MCI_STATUS_NUMBER_OF_TRACKS:
  861. if (!disc_ready(didDrive))
  862. return MCIERR_HARDWARE;
  863. lpStatus->dwReturn = (DWORD)CDA_num_tracks (didDrive);
  864. dwReturn = 0;
  865. break;
  866. case MCI_STATUS_CURRENT_TRACK:
  867. {
  868. redbook tracktime;
  869. if (!DriveTable[didDrive].bDiscPlayed)
  870. lpStatus->dwReturn = 1;
  871. else
  872. {
  873. if (CDA_time_info(didDrive, &tracktime, NULL) !=
  874. COMMAND_SUCCESSFUL)
  875. return MCIERR_HARDWARE;
  876. lpStatus->dwReturn = REDTRACK (tracktime);
  877. }
  878. break;
  879. }
  880. case MCI_CDA_STATUS_TYPE_TRACK:
  881. if (!disc_ready(didDrive))
  882. return MCIERR_HARDWARE;
  883. if (dwFlags & MCI_TRACK)
  884. {
  885. DWORD dwTmp;
  886. dwTmp = CDA_track_type (didDrive, (int)lpStatus->dwTrack);
  887. switch (dwTmp)
  888. {
  889. case INVALID_TRACK:
  890. return MCIERR_OUTOFRANGE;
  891. case MCI_CDA_TRACK_AUDIO:
  892. lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(dwTmp,
  893. MCI_CDA_AUDIO_S);
  894. break;
  895. case MCI_CDA_TRACK_OTHER:
  896. lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(dwTmp,
  897. MCI_CDA_OTHER_S);
  898. break;
  899. }
  900. dwReturn = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
  901. }
  902. break;
  903. case MCI_STATUS_TRACK_POS:
  904. {
  905. // Note: This code is a major hack that does an end-run around
  906. // past the normal MCI functionality. The only reason it
  907. // is here is because the new functionality replaces 3 MCI
  908. // calls in CDPLAYER to get the position,track, and status
  909. // with this one call.
  910. // This means what used to take ~15 IOCTL's to accomplish
  911. // now takes ~1 IOCTL. Since CDPLAYER generates one of
  912. // these messages every 1/2 second for updating it's timer
  913. // display. This is a major reduction in system traffic
  914. // for SCSI and IDE CD-Roms drivers.
  915. DWORD status;
  916. DWORD mciStatus;
  917. redbook tracktime, disctime;
  918. int rc;
  919. STATUSTRACKPOS stp;
  920. PSTATUSTRACKPOS pSTP;
  921. if (!DriveTable[didDrive].bDiscPlayed)
  922. {
  923. tracktime = REDTH(0, 1);
  924. if (!disc_ready(didDrive))
  925. {
  926. dprintf(("mcStatus (%08LX), MCI_STATUS_TRACK_POS, Disc Not Ready", (DWORD)didDrive));
  927. return MCIERR_HARDWARE;
  928. }
  929. disctime = CDA_track_start( didDrive, 1);
  930. status = CDA_drive_status (didDrive);
  931. switch (status)
  932. {
  933. case DISC_PLAYING:
  934. mciStatus = MCI_MODE_PLAY;
  935. break;
  936. case DISC_PAUSED:
  937. mciStatus = MCI_MODE_STOP; // HACK HACK!
  938. break;
  939. case DISC_READY:
  940. mciStatus = MCI_MODE_STOP;
  941. break;
  942. default:
  943. if (CDA_traystate (didDrive) == TRAY_OPEN)
  944. mciStatus = MCI_MODE_OPEN;
  945. else
  946. mciStatus = MCI_MODE_NOT_READY;
  947. break;
  948. }
  949. }
  950. else
  951. {
  952. rc = CDA_status_track_pos (didDrive, &status, &tracktime, &disctime);
  953. if (rc != COMMAND_SUCCESSFUL)
  954. {
  955. dprintf(("mcStatus (%08LX), MCI_STATUS_TRACK_POS, CDA_status_track_pos failed", (DWORD)didDrive));
  956. return MCIERR_HARDWARE;
  957. }
  958. if (REDTRACK(tracktime) == 0)
  959. {
  960. tracktime = (redbook)0;
  961. disctime = (redbook)0;
  962. }
  963. switch (status)
  964. {
  965. case DISC_PLAYING:
  966. mciStatus = MCI_MODE_PLAY;
  967. break;
  968. case DISC_PAUSED:
  969. mciStatus = MCI_MODE_STOP; // HACK HACK!
  970. break;
  971. case DISC_READY:
  972. mciStatus = MCI_MODE_STOP;
  973. break;
  974. case DISC_NOT_IN_CDROM:
  975. mciStatus = MCI_MODE_OPEN;
  976. break;
  977. default:
  978. mciStatus = MCI_MODE_NOT_READY;
  979. break;
  980. }
  981. }
  982. stp.dwStatus = mciStatus;
  983. stp.dwTrack = REDTRACK (tracktime);
  984. switch (pInst->dwTimeFormat)
  985. {
  986. case MCI_FORMAT_MILLISECONDS:
  987. stp.dwDiscTime = redtomil ((redbook)disctime);
  988. dwReturn = 0;
  989. break;
  990. case MCI_FORMAT_MSF:
  991. stp.dwDiscTime = flip3(disctime);
  992. dwReturn = MCI_COLONIZED3_RETURN;
  993. break;
  994. case MCI_FORMAT_TMSF:
  995. stp.dwDiscTime = flip4 (tracktime);
  996. dwReturn = MCI_COLONIZED4_RETURN;
  997. break;
  998. }
  999. pSTP = (PSTATUSTRACKPOS)lpStatus->dwReturn;
  1000. if (pSTP == NULL)
  1001. return MCIERR_MISSING_PARAMETER;
  1002. pSTP->dwStatus = stp.dwStatus;
  1003. pSTP->dwTrack = stp.dwTrack;
  1004. pSTP->dwDiscTime = stp.dwDiscTime;
  1005. break;
  1006. }
  1007. default:
  1008. dwReturn = MCIERR_UNSUPPORTED_FUNCTION;
  1009. break;
  1010. }
  1011. return dwReturn;
  1012. }
  1013. /*****************************************************************************
  1014. @doc INTERNAL MCICDA
  1015. @api DWORD | mcClose | Process the MCI_CLOSE command
  1016. @parm PINSTDATA | pInst | Pointer to application data instance
  1017. @rdesc
  1018. @comm
  1019. *****************************************************************************/
  1020. DWORD mcClose(
  1021. PINSTDATA pInst)
  1022. {
  1023. DID didDrive = pInst->uDevice;
  1024. MCIDEVICEID wDeviceID = pInst->uMCIDeviceID;
  1025. int nUseCount;
  1026. if (!pInst)
  1027. {
  1028. dprintf2(("mcClose, passed in NULL pointer"));
  1029. }
  1030. if (DriveTable[didDrive].nUseCount == 0)
  1031. {
  1032. dprintf2(("mcClose (%08lX), nUseCount already ZERO!!!", (DWORD)didDrive));
  1033. }
  1034. else if (--DriveTable[didDrive].nUseCount == 0)
  1035. {
  1036. dprintf2(("mcClose, Actually closing device (%08lX)", (DWORD)didDrive));
  1037. CDA_close(didDrive);
  1038. CDA_terminate_audio ();
  1039. }
  1040. else
  1041. {
  1042. dprintf2(("mcClose, Enter, device (%08lx), decremented useCount = %ld",
  1043. (DWORD)didDrive, DriveTable[didDrive].nUseCount));
  1044. // Note: Having this here prevents a mis-count problem
  1045. CDA_close(didDrive);
  1046. }
  1047. // Abort any notify if the use count is 0 or if the notify is for the device
  1048. // being closed
  1049. if ((DriveTable[didDrive].nUseCount == 0) ||
  1050. (wDeviceID == DriveTable[didDrive].wDeviceID))
  1051. abort_notify (pInst);
  1052. mciSetDriverData(pInst->uMCIDeviceID, 0L);
  1053. LocalFree((HLOCAL)pInst);
  1054. dprintf2(("mcClose, Exit, device (%08lx), useCount = %ld",
  1055. (DWORD)didDrive, DriveTable[didDrive].nUseCount));
  1056. return 0;
  1057. }
  1058. /*****************************************************************************
  1059. @doc INTERNAL MCICDA
  1060. @api DWORD | mcStop | Process the MCI_STOP command
  1061. @parm PINSTDATA | pInst | Pointer to application data instance
  1062. @parm DWORD | dwFlags |
  1063. @rdesc
  1064. *****************************************************************************/
  1065. DWORD mcStop(
  1066. PINSTDATA pInst,
  1067. DWORD dwFlags,
  1068. LPMCI_GENERIC_PARMS lpGeneric)
  1069. {
  1070. DID didDrive = pInst->uDevice;
  1071. if (!disc_ready (didDrive))
  1072. return MCIERR_HARDWARE;
  1073. abort_notify (pInst);
  1074. if (CDA_stop_audio(didDrive) != COMMAND_SUCCESSFUL)
  1075. return MCIERR_HARDWARE;
  1076. return 0;
  1077. }
  1078. /*****************************************************************************
  1079. @doc INTERNAL MCICDA
  1080. @api DWORD | mcPause | Process the MCI_PAUSE command
  1081. @parm PINSTDATA | pInst | Pointer to application data instance
  1082. @parm DWORD | dwFlags |
  1083. @rdesc
  1084. *****************************************************************************/
  1085. DWORD mcPause(
  1086. PINSTDATA pInst,
  1087. DWORD dwFlags,
  1088. LPMCI_GENERIC_PARMS lpGeneric)
  1089. {
  1090. DID didDrive = pInst->uDevice;
  1091. if (!disc_ready (didDrive))
  1092. return MCIERR_HARDWARE;
  1093. abort_notify (pInst);
  1094. if (CDA_pause_audio(didDrive) != COMMAND_SUCCESSFUL)
  1095. return MCIERR_HARDWARE;
  1096. return 0;
  1097. }
  1098. /*****************************************************************************
  1099. @doc INTERNAL MCICDA
  1100. @api DWORD | mcResume | Process the MCI_PAUSE command
  1101. @parm PINSTDATA | pInst | Pointer to application data instance
  1102. @parm DWORD | dwFlags |
  1103. @rdesc
  1104. *****************************************************************************/
  1105. DWORD mcResume(
  1106. PINSTDATA pInst,
  1107. DWORD dwFlags,
  1108. LPMCI_GENERIC_PARMS lpGeneric)
  1109. {
  1110. DID didDrive = pInst->uDevice;
  1111. if (!disc_ready (didDrive))
  1112. return MCIERR_HARDWARE;
  1113. abort_notify (pInst);
  1114. if (CDA_resume_audio(didDrive) != COMMAND_SUCCESSFUL)
  1115. return MCIERR_HARDWARE;
  1116. return 0;
  1117. }
  1118. // MBR cda.c!SendDriverReq masks off the actual error bits and just
  1119. // leaves the upper bit set - this is ok for now. There exists
  1120. // no seperate "command is known but not supported" error at
  1121. // the driver level, so if the driver returns "unrecognized
  1122. // command", we return "unsupported function".
  1123. #define ERRQ(X) (((X)==0) ? MCIERR_UNSUPPORTED_FUNCTION : 0)
  1124. /*****************************************************************************
  1125. @doc INTERNAL MCICDA
  1126. @api DWORD | mcSet | Process the MCI_SET command
  1127. @parm DWORD | dwFlags |
  1128. @parm LPMCI_SET_PARMS | lpSet |
  1129. @rdesc
  1130. @comm
  1131. *****************************************************************************/
  1132. DWORD mcSet(
  1133. PINSTDATA pInst,
  1134. DWORD dwFlags,
  1135. LPMCI_SET_PARMS lpSet )
  1136. {
  1137. DID didDrive = pInst->uDevice;
  1138. UINT wErr = 0;
  1139. dwFlags &= ~(MCI_NOTIFY | MCI_WAIT);
  1140. if (!dwFlags)
  1141. return MCIERR_MISSING_PARAMETER;
  1142. if (dwFlags & MCI_SET_TIME_FORMAT)
  1143. {
  1144. DWORD wFormat = lpSet->dwTimeFormat;
  1145. switch (wFormat)
  1146. {
  1147. case MCI_FORMAT_MILLISECONDS:
  1148. case MCI_FORMAT_MSF:
  1149. case MCI_FORMAT_TMSF:
  1150. pInst->dwTimeFormat = wFormat;
  1151. break;
  1152. default:
  1153. wErr = MCIERR_BAD_TIME_FORMAT;
  1154. break;
  1155. }
  1156. }
  1157. if (!wErr && (dwFlags & MCI_SET_DOOR_OPEN))
  1158. {
  1159. abort_notify (pInst);
  1160. CDA_stop_audio (didDrive);
  1161. CDA_eject(didDrive);
  1162. DriveTable[didDrive].bDiscPlayed = FALSE;
  1163. }
  1164. if (!wErr && (dwFlags & MCI_SET_AUDIO))
  1165. {
  1166. UCHAR wVolume;
  1167. if (dwFlags & MCI_SET_ON && dwFlags & MCI_SET_OFF)
  1168. return MCIERR_FLAGS_NOT_COMPATIBLE;
  1169. if (dwFlags & MCI_SET_ON)
  1170. wVolume = 255;
  1171. else if (dwFlags & MCI_SET_OFF)
  1172. wVolume = 0;
  1173. else
  1174. return MCIERR_MISSING_PARAMETER;
  1175. switch (lpSet->dwAudio)
  1176. {
  1177. case MCI_SET_AUDIO_ALL:
  1178. if (CDA_set_audio_volume_all (didDrive, wVolume)
  1179. != COMMAND_SUCCESSFUL)
  1180. wErr = MCIERR_HARDWARE;
  1181. break;
  1182. case MCI_SET_AUDIO_LEFT:
  1183. if (CDA_set_audio_volume (didDrive, 0, wVolume)
  1184. != COMMAND_SUCCESSFUL)
  1185. wErr = MCIERR_HARDWARE;
  1186. break;
  1187. case MCI_SET_AUDIO_RIGHT:
  1188. if (CDA_set_audio_volume (didDrive, 1, wVolume)
  1189. != COMMAND_SUCCESSFUL)
  1190. wErr = MCIERR_HARDWARE;
  1191. break;
  1192. }
  1193. }
  1194. if (!wErr && dwFlags & MCI_SET_DOOR_CLOSED)
  1195. CDA_closetray (didDrive);
  1196. return wErr;
  1197. }
  1198. /*****************************************************************************
  1199. @doc INTERNAL MCICDA
  1200. @api DWORD | mcInfo | Process the MCI_INFO command
  1201. @parm PINSTDATA | pInst | Pointer to application instance data
  1202. @parm DWORD | dwFlags |
  1203. @parm LPMCI_INFO_PARMS | lpInfo |
  1204. @rdesc
  1205. @comm
  1206. *****************************************************************************/
  1207. DWORD mcInfo (
  1208. PINSTDATA pInst,
  1209. DWORD dwFlags,
  1210. LPMCI_INFO_PARMS lpInfo )
  1211. {
  1212. DID didDrive = pInst->uDevice;
  1213. DWORD wReturnBufferLength;
  1214. wReturnBufferLength = LOWORD(lpInfo->dwRetSize);
  1215. if (!lpInfo->lpstrReturn || !wReturnBufferLength)
  1216. return MCIERR_PARAM_OVERFLOW;
  1217. if (dwFlags & MCI_INFO_PRODUCT)
  1218. {
  1219. *(lpInfo->lpstrReturn) = '\0';
  1220. lpInfo->dwRetSize = (DWORD)LoadString(hInstance, IDS_PRODUCTNAME, lpInfo->lpstrReturn, (int)wReturnBufferLength);
  1221. return 0;
  1222. }
  1223. else if (dwFlags & MCI_INFO_MEDIA_UPC)
  1224. {
  1225. unsigned char upc[16];
  1226. int i;
  1227. if (!disc_ready(didDrive))
  1228. return MCIERR_HARDWARE;
  1229. if (CDA_disc_upc(didDrive, lpInfo->lpstrReturn) != COMMAND_SUCCESSFUL)
  1230. return MCIERR_NO_IDENTITY;
  1231. return 0;
  1232. }
  1233. else if (dwFlags & MCI_INFO_MEDIA_IDENTITY)
  1234. {
  1235. DWORD dwId;
  1236. if (!disc_ready(didDrive))
  1237. return MCIERR_HARDWARE;
  1238. dwId = CDA_disc_id(didDrive);
  1239. if (dwId == (DWORD)-1L)
  1240. return MCIERR_HARDWARE;
  1241. wsprintf(lpInfo->lpstrReturn,TEXT("%lu"),dwId);
  1242. return 0;
  1243. } else
  1244. return MCIERR_MISSING_PARAMETER;
  1245. }
  1246. /*
  1247. * @doc INTERNAL MCIRBOOK
  1248. *
  1249. * @api DWORD | mciDriverEntry | Single entry point for MCI drivers
  1250. *
  1251. * @parm MCIDEVICEID | wDeviceID | The MCI device ID
  1252. *
  1253. * @parm UINT | message | The requested action to be performed.
  1254. *
  1255. * @parm LPARAM | lParam1 | Data for this message. Defined seperately for
  1256. * each message
  1257. *
  1258. * @parm LPARAM | lParam2 | Data for this message. Defined seperately for
  1259. * each message
  1260. *
  1261. * @rdesc Defined seperately for each message.
  1262. *
  1263. */
  1264. DWORD CD_MCI_Handler (MCIDEVICEID wDeviceID,
  1265. UINT message,
  1266. DWORD_PTR lParam1,
  1267. DWORD_PTR lParam2)
  1268. {
  1269. DID didDrive;
  1270. LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)lParam2;
  1271. BOOL bDelayed = FALSE;
  1272. DWORD dwErr = 0, wNotifyErr;
  1273. DWORD dwPlayTo = MCICDA_BAD_TIME;
  1274. WORD wNotifyStatus = MCI_NOTIFY_SUCCESSFUL;
  1275. PINSTDATA pInst;
  1276. pInst = (PINSTDATA)mciGetDriverData(wDeviceID);
  1277. didDrive = (DID)pInst->uDevice;
  1278. EnterCrit( CdInfo[didDrive].DeviceCritSec );
  1279. switch (message)
  1280. {
  1281. case MCI_OPEN_DRIVER:
  1282. dwErr = mcOpen (pInst, (DWORD)lParam1, (LPMCI_OPEN_PARMS)lParam2);
  1283. break;
  1284. case MCI_CLOSE_DRIVER:
  1285. dwErr = mcClose (pInst);
  1286. break;
  1287. case MCI_PLAY:
  1288. {
  1289. BOOL bBreak = FALSE;
  1290. dwErr = mcPlay (pInst, (DWORD)lParam1, (LPMCI_PLAY_PARMS)lParam2, &bBreak);
  1291. if (dwErr == 0 && (DWORD)lParam1 & MCI_WAIT && (DWORD)lParam1 & MCI_NOTIFY)
  1292. {
  1293. switch (CDA_drive_status (didDrive))
  1294. {
  1295. case DISC_PLAYING:
  1296. case DISC_PAUSED:
  1297. case DISC_READY:
  1298. break;
  1299. default:
  1300. wNotifyStatus = MCI_NOTIFY_FAILURE;
  1301. break;
  1302. }
  1303. }
  1304. // If MCI_WAIT is not set or if the wait loop was broken out of then delay
  1305. if (!((DWORD)lParam1 & MCI_WAIT) || bBreak)
  1306. bDelayed = TRUE;
  1307. break;
  1308. }
  1309. case MCI_SEEK:
  1310. dwErr = mcSeek (pInst, (DWORD)lParam1, (LPMCI_SEEK_PARMS)lParam2);
  1311. break;
  1312. case MCI_STOP:
  1313. dwErr = mcStop ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2);
  1314. break;
  1315. case MCI_PAUSE:
  1316. dwErr = mcPause ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2);
  1317. break;
  1318. case MCI_GETDEVCAPS:
  1319. dwErr = mcGetDevCaps (pInst, (DWORD)lParam1, (LPMCI_GETDEVCAPS_PARMS)lParam2);
  1320. break;
  1321. case MCI_STATUS:
  1322. dwErr = mcStatus (pInst, (DWORD)lParam1, (LPMCI_STATUS_PARMS)lParam2);
  1323. break;
  1324. case MCI_SET:
  1325. dwErr = mcSet (pInst, (DWORD)lParam1, (LPMCI_SET_PARMS)lParam2);
  1326. break;
  1327. case MCI_INFO:
  1328. dwErr = mcInfo (pInst, (DWORD)lParam1, (LPMCI_INFO_PARMS)lParam2);
  1329. break;
  1330. case MCI_RECORD:
  1331. case MCI_LOAD:
  1332. case MCI_SAVE:
  1333. LeaveCrit( CdInfo[didDrive].DeviceCritSec );
  1334. return MCIERR_UNSUPPORTED_FUNCTION;
  1335. case MCI_RESUME:
  1336. dwErr = mcResume ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2);
  1337. break;
  1338. default:
  1339. LeaveCrit( CdInfo[didDrive].DeviceCritSec );
  1340. return MCIERR_UNRECOGNIZED_COMMAND;
  1341. } /* switch */
  1342. /* it is possible that the instance information has disappeared if
  1343. * CLOSE NOTIFY is requested. Therefore NOTIFY should never take
  1344. * instance data.
  1345. */
  1346. if ((DWORD)lParam1 & MCI_NOTIFY && LOWORD (dwErr) == 0)
  1347. if ((wNotifyErr =
  1348. notify (didDrive, wDeviceID, bDelayed, wNotifyStatus,
  1349. (LPMCI_GENERIC_PARMS)lParam2)) != 0) {
  1350. LeaveCrit( CdInfo[didDrive].DeviceCritSec );
  1351. return wNotifyErr;
  1352. }
  1353. LeaveCrit( CdInfo[didDrive].DeviceCritSec );
  1354. return dwErr;
  1355. }