1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * MCI driver for audio CD (MCICDA)
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1998-99 Eric Pouech
7 * Copyright 2000 Andreas Mohr
22 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(mcicda);
26 #define CDFRAMES_PERSEC 75
27 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
28 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
29 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
33 int nUseCount; /* Incremented for each shared open */
34 BOOL fShareable; /* TRUE if first open was shareable */
35 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
36 HANDLE hCallback; /* Callback handle for pending notification */
41 /*-----------------------------------------------------------------------*/
43 /**************************************************************************
44 * MCICDA_drvOpen [internal]
46 static DWORD MCICDA_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
48 WINE_MCICDAUDIO* wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
53 wmcda->wDevID = modp->wDeviceID;
54 mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
55 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
56 modp->wType = MCI_DEVTYPE_CD_AUDIO;
57 return modp->wDeviceID;
60 /**************************************************************************
61 * MCICDA_drvClose [internal]
63 static DWORD MCICDA_drvClose(DWORD dwDevID)
65 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
68 HeapFree(GetProcessHeap(), 0, wmcda);
69 mciSetDriverData(dwDevID, 0);
74 /**************************************************************************
75 * MCICDA_GetOpenDrv [internal]
77 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
79 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
81 if (wmcda == NULL || wmcda->nUseCount == 0) {
82 WARN("Invalid wDevID=%u\n", wDevID);
88 /**************************************************************************
89 * MCICDA_GetStatus [internal]
91 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
93 CDROM_SUB_Q_DATA_FORMAT fmt;
94 SUB_Q_CHANNEL_DATA data;
96 DWORD mode = MCI_MODE_NOT_READY;
98 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
99 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
100 &data, sizeof(data), &br, NULL)) {
101 if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
103 switch (data.CurrentPosition.Header.AudioStatus)
105 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
106 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
107 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
108 case AUDIO_STATUS_PLAY_ERROR:
109 case AUDIO_STATUS_NOT_SUPPORTED:
110 case AUDIO_STATUS_NO_STATUS:
118 /**************************************************************************
119 * MCICDA_GetError [internal]
121 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
123 switch (GetLastError())
125 case STATUS_NO_MEDIA_IN_DEVICE: return MCIERR_DEVICE_NOT_READY;
126 case STATUS_IO_DEVICE_ERROR: return MCIERR_HARDWARE;
128 FIXME("Unknown mode %lx\n", GetLastError());
130 return MCIERR_DRIVER_INTERNAL;
133 /**************************************************************************
134 * MCICDA_CalcFrame [internal]
136 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
144 TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
146 switch (wmcda->dwTimeFormat) {
147 case MCI_FORMAT_MILLISECONDS:
148 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
149 TRACE("MILLISECONDS %lu\n", dwFrame);
152 TRACE("MSF %02u:%02u:%02u\n",
153 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
154 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
155 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
156 dwFrame += MCI_MSF_FRAME(dwTime);
158 case MCI_FORMAT_TMSF:
159 default: /* unknown format ! force TMSF ! ... */
160 wTrack = MCI_TMSF_TRACK(dwTime);
161 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
162 &toc, sizeof(toc), &br, NULL))
164 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
166 TRACE("MSF %02u-%02u:%02u:%02u\n",
167 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
168 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
169 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
170 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
171 wTrack, addr[0], addr[1], addr[2]);
172 dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
173 CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
174 addr[2] + MCI_TMSF_FRAME(dwTime);
180 /**************************************************************************
181 * MCICDA_CalcTime [internal]
183 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
193 TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
196 case MCI_FORMAT_MILLISECONDS:
197 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
198 TRACE("MILLISECONDS %lu\n", dwTime);
202 wMinutes = dwFrame / CDFRAMES_PERMIN;
203 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
204 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
205 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
206 TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
207 wMinutes, wSeconds, wFrames, dwTime);
208 *lpRet = MCI_COLONIZED3_RETURN;
210 case MCI_FORMAT_TMSF:
211 default: /* unknown format ! force TMSF ! ... */
212 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
213 &toc, sizeof(toc), &br, NULL))
215 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
216 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
217 ERR("Out of range value %lu [%u,%u]\n",
218 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
219 FRAME_OF_TOC(toc, toc.LastTrack + 1));
223 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
224 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
228 dwFrame -= FRAME_OF_TOC(toc, wTrack);
229 wMinutes = dwFrame / CDFRAMES_PERMIN;
230 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
231 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
232 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
233 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
234 *lpRet = MCI_COLONIZED4_RETURN;
240 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
241 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
243 /**************************************************************************
244 * MCICDA_Open [internal]
246 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
249 DWORD ret = MCIERR_HARDWARE;
250 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
255 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
257 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
258 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
260 dwDeviceID = lpOpenParms->wDeviceID;
262 if (wmcda->nUseCount > 0) {
263 /* The driver is already open on this channel */
264 /* If the driver was opened shareable before and this open specifies */
265 /* shareable then increment the use count */
266 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
269 return MCIERR_MUST_USE_SHAREABLE;
271 wmcda->nUseCount = 1;
272 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
274 if (dwFlags & MCI_OPEN_ELEMENT) {
275 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
276 WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
277 return MCIERR_NO_ELEMENT_ALLOWED;
279 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
280 lpOpenParms->lpstrElementName[2])
282 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms->lpstrElementName);
283 ret = MCIERR_NO_ELEMENT_ALLOWED;
286 drive = toupper(lpOpenParms->lpstrElementName[0]);
287 strcpy(root, "A:\\");
289 if (GetDriveTypeA(root) != DRIVE_CDROM)
291 ret = MCIERR_INVALID_DEVICE_NAME;
297 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
298 strcpy(root, "A:\\");
299 for (count = 0; root[0] <= 'Z'; root[0]++)
301 if (GetDriveTypeA(root) == DRIVE_CDROM && ++count >= dwDeviceID)
309 ret = MCIERR_INVALID_DEVICE_ID;
314 wmcda->wNotifyDeviceID = dwDeviceID;
315 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
317 /* now, open the handle */
318 strcpy(root, "\\\\.\\A:");
320 wmcda->handle = CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
321 if (wmcda->handle != INVALID_HANDLE_VALUE)
329 /**************************************************************************
330 * MCICDA_Close [internal]
332 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
334 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
336 TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
338 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
340 if (--wmcda->nUseCount == 0) {
341 CloseHandle(wmcda->handle);
346 /**************************************************************************
347 * MCICDA_GetDevCaps [internal]
349 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
350 LPMCI_GETDEVCAPS_PARMS lpParms)
354 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
356 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
358 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
359 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
361 switch (lpParms->dwItem) {
362 case MCI_GETDEVCAPS_CAN_RECORD:
363 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
364 ret = MCI_RESOURCE_RETURNED;
366 case MCI_GETDEVCAPS_HAS_AUDIO:
367 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
368 ret = MCI_RESOURCE_RETURNED;
370 case MCI_GETDEVCAPS_HAS_VIDEO:
371 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
372 ret = MCI_RESOURCE_RETURNED;
374 case MCI_GETDEVCAPS_DEVICE_TYPE:
375 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
376 ret = MCI_RESOURCE_RETURNED;
378 case MCI_GETDEVCAPS_USES_FILES:
379 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
380 ret = MCI_RESOURCE_RETURNED;
382 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
383 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
384 ret = MCI_RESOURCE_RETURNED;
386 case MCI_GETDEVCAPS_CAN_EJECT:
387 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
388 ret = MCI_RESOURCE_RETURNED;
390 case MCI_GETDEVCAPS_CAN_PLAY:
391 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
392 ret = MCI_RESOURCE_RETURNED;
394 case MCI_GETDEVCAPS_CAN_SAVE:
395 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
396 ret = MCI_RESOURCE_RETURNED;
399 ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
400 return MCIERR_UNRECOGNIZED_COMMAND;
403 TRACE("No GetDevCaps-Item !\n");
404 return MCIERR_UNRECOGNIZED_COMMAND;
406 TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
410 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
412 unsigned long serial = 0;
415 DWORD dwStart, dwEnd;
418 * wMagic collects the wFrames from track 1
419 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
421 * There it is collected for correcting the serial when there are less than
424 wMagic = toc->TrackData[0].Address[3];
425 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
427 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
428 serial += (toc->TrackData[i].Address[1] << 16) |
429 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
431 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
433 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
434 serial += wMagic + (dwEnd - dwStart);
440 /**************************************************************************
441 * MCICDA_Info [internal]
443 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
446 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
450 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
452 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
453 return MCIERR_NULL_PARAMETER_BLOCK;
454 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
456 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
458 if (dwFlags & MCI_INFO_PRODUCT) {
459 str = "Wine's audio CD";
460 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
461 ret = MCIERR_NO_IDENTITY;
462 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
467 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
468 &toc, sizeof(toc), &br, NULL)) {
469 return MCICDA_GetError(wmcda);
472 res = CDROM_Audio_GetSerial(&toc);
473 sprintf(buffer, "%lu", res);
476 WARN("Don't know this info command (%lu)\n", dwFlags);
477 ret = MCIERR_UNRECOGNIZED_COMMAND;
480 if (lpParms->dwRetSize <= strlen(str)) {
481 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
482 ret = MCIERR_PARAM_OVERFLOW;
484 strcpy(lpParms->lpstrReturn, str);
487 *lpParms->lpstrReturn = 0;
489 TRACE("=> %s (%ld)\n", lpParms->lpstrReturn, ret);
493 /**************************************************************************
494 * MCICDA_Status [internal]
496 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
498 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
501 CDROM_SUB_Q_DATA_FORMAT fmt;
502 SUB_Q_CHANNEL_DATA data;
506 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
508 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
509 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
511 if (dwFlags & MCI_NOTIFY) {
512 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
513 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
514 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
516 if (dwFlags & MCI_STATUS_ITEM) {
517 TRACE("dwItem = %lx\n", lpParms->dwItem);
518 switch (lpParms->dwItem) {
519 case MCI_STATUS_CURRENT_TRACK:
520 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
521 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
522 &data, sizeof(data), &br, NULL))
524 return MCICDA_GetError(wmcda);
526 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
527 TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
529 case MCI_STATUS_LENGTH:
530 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
531 &toc, sizeof(toc), &br, NULL)) {
532 WARN("error reading TOC !\n");
533 return MCICDA_GetError(wmcda);
535 if (dwFlags & MCI_TRACK) {
536 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
537 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
538 return MCIERR_OUTOFRANGE;
539 idx = lpParms->dwTrack - toc.FirstTrack;
540 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
541 FRAME_OF_TOC(toc, lpParms->dwTrack);
542 /* Windows returns one frame less than the total track length for the
543 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
544 if (lpParms->dwTrack == toc.LastTrack)
547 /* Sum of the lengths of all of the tracks. Inherits the
548 'off by one frame' behavior from the length of the last track.
549 See above comment. */
550 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
551 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
553 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
554 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
555 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
558 TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
560 case MCI_STATUS_MODE:
561 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
562 TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
563 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
564 ret = MCI_RESOURCE_RETURNED;
566 case MCI_STATUS_MEDIA_PRESENT:
567 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
568 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
569 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
570 ret = MCI_RESOURCE_RETURNED;
572 case MCI_STATUS_NUMBER_OF_TRACKS:
573 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
574 &toc, sizeof(toc), &br, NULL)) {
575 WARN("error reading TOC !\n");
576 return MCICDA_GetError(wmcda);
578 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
579 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
580 if (lpParms->dwReturn == (WORD)-1)
581 return MCICDA_GetError(wmcda);
583 case MCI_STATUS_POSITION:
584 if (dwFlags & MCI_STATUS_START) {
585 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
586 &toc, sizeof(toc), &br, NULL)) {
587 WARN("error reading TOC !\n");
588 return MCICDA_GetError(wmcda);
590 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
591 TRACE("get MCI_STATUS_START !\n");
592 } else if (dwFlags & MCI_TRACK) {
593 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
594 &toc, sizeof(toc), &br, NULL)) {
595 WARN("error reading TOC !\n");
596 return MCICDA_GetError(wmcda);
598 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
599 return MCIERR_OUTOFRANGE;
600 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
601 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
603 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
604 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
605 &data, sizeof(data), &br, NULL)) {
606 return MCICDA_GetError(wmcda);
608 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
610 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
611 TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
613 case MCI_STATUS_READY:
614 TRACE("MCI_STATUS_READY !\n");
615 switch (MCICDA_GetStatus(wmcda))
617 case MCI_MODE_NOT_READY:
619 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
622 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
625 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
626 ret = MCI_RESOURCE_RETURNED;
628 case MCI_STATUS_TIME_FORMAT:
629 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, wmcda->dwTimeFormat);
630 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
631 ret = MCI_RESOURCE_RETURNED;
633 case 4001: /* FIXME: for bogus FullCD */
634 case MCI_CDA_STATUS_TYPE_TRACK:
635 if (!(dwFlags & MCI_TRACK))
636 ret = MCIERR_MISSING_PARAMETER;
638 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
639 &toc, sizeof(toc), &br, NULL)) {
640 WARN("error reading TOC !\n");
641 return MCICDA_GetError(wmcda);
643 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
644 ret = MCIERR_OUTOFRANGE;
646 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
647 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
649 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
652 FIXME("unknown command %08lX !\n", lpParms->dwItem);
653 return MCIERR_UNRECOGNIZED_COMMAND;
656 WARN("not MCI_STATUS_ITEM !\n");
661 /**************************************************************************
662 * MCICDA_Play [internal]
664 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
666 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
667 DWORD ret = 0, start, end;
670 CDROM_PLAY_AUDIO_MSF play;
671 CDROM_SUB_Q_DATA_FORMAT fmt;
672 SUB_Q_CHANNEL_DATA data;
674 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
677 return MCIERR_NULL_PARAMETER_BLOCK;
680 return MCIERR_INVALID_DEVICE_ID;
682 if (dwFlags & MCI_FROM) {
683 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
684 TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
686 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
687 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
688 &data, sizeof(data), &br, NULL)) {
689 return MCICDA_GetError(wmcda);
691 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
693 if (dwFlags & MCI_TO) {
694 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
695 TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
697 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
698 &toc, sizeof(toc), &br, NULL)) {
699 WARN("error reading TOC !\n");
700 return MCICDA_GetError(wmcda);
702 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
704 TRACE("Playing from %lu to %lu\n", start, end);
705 play.StartingM = start / CDFRAMES_PERMIN;
706 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
707 play.StartingF = start % CDFRAMES_PERSEC;
708 play.EndingM = end / CDFRAMES_PERMIN;
709 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
710 play.EndingF = end % CDFRAMES_PERSEC;
711 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
712 NULL, 0, &br, NULL)) {
713 ret = MCIERR_HARDWARE;
714 } else if (dwFlags & MCI_NOTIFY) {
715 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
717 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
718 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
724 /**************************************************************************
725 * MCICDA_Stop [internal]
727 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
729 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
732 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
734 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
736 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
737 return MCIERR_HARDWARE;
739 if (lpParms && (dwFlags & MCI_NOTIFY)) {
740 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
741 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
742 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
747 /**************************************************************************
748 * MCICDA_Pause [internal]
750 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
752 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
755 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
757 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
759 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
760 return MCIERR_HARDWARE;
762 if (lpParms && (dwFlags & MCI_NOTIFY)) {
763 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
764 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
765 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
770 /**************************************************************************
771 * MCICDA_Resume [internal]
773 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
775 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
778 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
780 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
782 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
783 return MCIERR_HARDWARE;
785 if (lpParms && (dwFlags & MCI_NOTIFY)) {
786 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
787 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
788 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
793 /**************************************************************************
794 * MCICDA_Seek [internal]
796 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
799 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
800 CDROM_SEEK_AUDIO_MSF seek;
804 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
806 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
807 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
809 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
810 case MCI_SEEK_TO_START:
811 TRACE("Seeking to start\n");
812 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
813 &toc, sizeof(toc), &br, NULL)) {
814 WARN("error reading TOC !\n");
815 return MCICDA_GetError(wmcda);
817 at = FRAME_OF_TOC(toc, toc.FirstTrack);
819 case MCI_SEEK_TO_END:
820 TRACE("Seeking to end\n");
821 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
822 &toc, sizeof(toc), &br, NULL)) {
823 WARN("error reading TOC !\n");
824 return MCICDA_GetError(wmcda);
826 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
829 TRACE("Seeking to %lu\n", lpParms->dwTo);
833 TRACE("Unknown seek action %08lX\n",
834 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
835 return MCIERR_UNSUPPORTED_FUNCTION;
837 seek.M = at / CDFRAMES_PERMIN;
838 seek.S = (at / CDFRAMES_PERSEC) % 60;
839 seek.F = at % CDFRAMES_PERSEC;
840 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
842 return MCIERR_HARDWARE;
844 if (dwFlags & MCI_NOTIFY) {
845 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
846 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
847 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
852 /**************************************************************************
853 * MCICDA_SetDoor [internal]
855 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
857 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
860 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
862 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
864 if (!DeviceIoControl(wmcda->handle,
865 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
866 NULL, 0, NULL, 0, &br, NULL))
867 return MCIERR_HARDWARE;
872 /**************************************************************************
873 * MCICDA_Set [internal]
875 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
877 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
879 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
881 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
883 if (dwFlags & MCI_SET_DOOR_OPEN) {
884 MCICDA_SetDoor(wDevID, TRUE);
886 if (dwFlags & MCI_SET_DOOR_CLOSED) {
887 MCICDA_SetDoor(wDevID, FALSE);
890 /* only functions which require valid lpParms below this line ! */
891 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
893 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
894 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
896 if (dwFlags & MCI_SET_TIME_FORMAT) {
897 switch (lpParms->dwTimeFormat) {
898 case MCI_FORMAT_MILLISECONDS:
899 TRACE("MCI_FORMAT_MILLISECONDS !\n");
902 TRACE("MCI_FORMAT_MSF !\n");
904 case MCI_FORMAT_TMSF:
905 TRACE("MCI_FORMAT_TMSF !\n");
908 WARN("bad time format !\n");
909 return MCIERR_BAD_TIME_FORMAT;
911 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
913 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
914 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
915 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
916 if (dwFlags & MCI_NOTIFY) {
917 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
918 lpParms->dwCallback);
919 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
920 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
925 /**************************************************************************
926 * DriverProc (MCICDA.@)
928 LONG CALLBACK MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
929 DWORD dwParam1, DWORD dwParam2)
932 case DRV_LOAD: return 1;
933 case DRV_FREE: return 1;
934 case DRV_OPEN: return MCICDA_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
935 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
936 case DRV_ENABLE: return 1;
937 case DRV_DISABLE: return 1;
938 case DRV_QUERYCONFIGURE: return 1;
939 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
940 case DRV_INSTALL: return DRVCNF_RESTART;
941 case DRV_REMOVE: return DRVCNF_RESTART;
943 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
944 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
945 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
946 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSA)dwParam2);
947 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
948 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
949 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
950 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
951 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
952 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
953 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
954 /* FIXME: I wonder if those two next items are really called ? */
955 case MCI_SET_DOOR_OPEN: FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
956 return MCICDA_SetDoor(dwDevID, TRUE);
957 case MCI_SET_DOOR_CLOSED: FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
958 return MCICDA_SetDoor(dwDevID, FALSE);
959 /* commands that should be supported */
975 FIXME("Unsupported yet command [%lu]\n", wMsg);
977 /* commands that should report an error */
979 TRACE("Unsupported command [%lu]\n", wMsg);
983 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
986 TRACE("Sending msg [%lu] to default driver proc\n", wMsg);
987 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
989 return MCIERR_UNRECOGNIZED_COMMAND;
992 /*-----------------------------------------------------------------------*/