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
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
41 #define CDFRAMES_PERSEC 75
42 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
43 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
44 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
48 int nUseCount; /* Incremented for each shared open */
49 BOOL fShareable; /* TRUE if first open was shareable */
50 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
51 HANDLE hCallback; /* Callback handle for pending notification */
56 /*-----------------------------------------------------------------------*/
58 /**************************************************************************
59 * MCICDA_drvOpen [internal]
61 static DWORD MCICDA_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
63 WINE_MCICDAUDIO* wmcda;
65 if (!modp) return 0xFFFFFFFF;
67 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
72 wmcda->wDevID = modp->wDeviceID;
73 mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
74 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
75 modp->wType = MCI_DEVTYPE_CD_AUDIO;
76 return modp->wDeviceID;
79 /**************************************************************************
80 * MCICDA_drvClose [internal]
82 static DWORD MCICDA_drvClose(DWORD dwDevID)
84 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
87 HeapFree(GetProcessHeap(), 0, wmcda);
88 mciSetDriverData(dwDevID, 0);
90 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
93 /**************************************************************************
94 * MCICDA_GetOpenDrv [internal]
96 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
98 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
100 if (wmcda == NULL || wmcda->nUseCount == 0) {
101 WARN("Invalid wDevID=%u\n", wDevID);
107 /**************************************************************************
108 * MCICDA_GetStatus [internal]
110 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
112 CDROM_SUB_Q_DATA_FORMAT fmt;
113 SUB_Q_CHANNEL_DATA data;
115 DWORD mode = MCI_MODE_NOT_READY;
117 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
118 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
119 &data, sizeof(data), &br, NULL)) {
120 if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
122 switch (data.CurrentPosition.Header.AudioStatus)
124 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
125 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
126 case AUDIO_STATUS_NO_STATUS:
127 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
128 case AUDIO_STATUS_PLAY_ERROR:
129 case AUDIO_STATUS_NOT_SUPPORTED:
137 /**************************************************************************
138 * MCICDA_GetError [internal]
140 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
142 switch (GetLastError())
144 case STATUS_NO_MEDIA_IN_DEVICE: return MCIERR_DEVICE_NOT_READY;
145 case STATUS_IO_DEVICE_ERROR: return MCIERR_HARDWARE;
147 FIXME("Unknown mode %lx\n", GetLastError());
149 return MCIERR_DRIVER_INTERNAL;
152 /**************************************************************************
153 * MCICDA_CalcFrame [internal]
155 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
163 TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
165 switch (wmcda->dwTimeFormat) {
166 case MCI_FORMAT_MILLISECONDS:
167 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
168 TRACE("MILLISECONDS %lu\n", dwFrame);
171 TRACE("MSF %02u:%02u:%02u\n",
172 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
173 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
174 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
175 dwFrame += MCI_MSF_FRAME(dwTime);
177 case MCI_FORMAT_TMSF:
178 default: /* unknown format ! force TMSF ! ... */
179 wTrack = MCI_TMSF_TRACK(dwTime);
180 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
181 &toc, sizeof(toc), &br, NULL))
183 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
185 TRACE("MSF %02u-%02u:%02u:%02u\n",
186 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
187 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
188 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
189 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
190 wTrack, addr[0], addr[1], addr[2]);
191 dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
192 CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
193 addr[2] + MCI_TMSF_FRAME(dwTime);
199 /**************************************************************************
200 * MCICDA_CalcTime [internal]
202 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
212 TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
215 case MCI_FORMAT_MILLISECONDS:
216 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
217 TRACE("MILLISECONDS %lu\n", dwTime);
221 wMinutes = dwFrame / CDFRAMES_PERMIN;
222 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
223 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
224 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
225 TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
226 wMinutes, wSeconds, wFrames, dwTime);
227 *lpRet = MCI_COLONIZED3_RETURN;
229 case MCI_FORMAT_TMSF:
230 default: /* unknown format ! force TMSF ! ... */
231 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
232 &toc, sizeof(toc), &br, NULL))
234 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
235 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
236 ERR("Out of range value %lu [%u,%u]\n",
237 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
238 FRAME_OF_TOC(toc, toc.LastTrack + 1));
242 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
243 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
247 dwFrame -= FRAME_OF_TOC(toc, wTrack);
248 wMinutes = dwFrame / CDFRAMES_PERMIN;
249 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
250 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
251 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
252 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
253 *lpRet = MCI_COLONIZED4_RETURN;
259 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
260 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
262 /**************************************************************************
263 * MCICDA_Open [internal]
265 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
268 DWORD ret = MCIERR_HARDWARE;
269 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
274 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
276 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
277 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
279 dwDeviceID = lpOpenParms->wDeviceID;
281 if (wmcda->nUseCount > 0) {
282 /* The driver is already open on this channel */
283 /* If the driver was opened shareable before and this open specifies */
284 /* shareable then increment the use count */
285 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
288 return MCIERR_MUST_USE_SHAREABLE;
290 wmcda->nUseCount = 1;
291 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
293 if (dwFlags & MCI_OPEN_ELEMENT) {
294 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
295 WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
296 return MCIERR_NO_ELEMENT_ALLOWED;
298 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
299 lpOpenParms->lpstrElementName[2])
301 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms->lpstrElementName);
302 ret = MCIERR_NO_ELEMENT_ALLOWED;
305 drive = toupper(lpOpenParms->lpstrElementName[0]);
306 strcpy(root, "A:\\");
308 if (GetDriveTypeA(root) != DRIVE_CDROM)
310 ret = MCIERR_INVALID_DEVICE_NAME;
316 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
317 strcpy(root, "A:\\");
318 for (count = 0; root[0] <= 'Z'; root[0]++)
320 if (GetDriveTypeA(root) == DRIVE_CDROM && ++count >= dwDeviceID)
328 ret = MCIERR_INVALID_DEVICE_ID;
333 wmcda->wNotifyDeviceID = dwDeviceID;
334 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
336 /* now, open the handle */
337 strcpy(root, "\\\\.\\A:");
339 wmcda->handle = CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
340 if (wmcda->handle != INVALID_HANDLE_VALUE)
348 /**************************************************************************
349 * MCICDA_Close [internal]
351 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
353 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
355 TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
357 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
359 if (--wmcda->nUseCount == 0) {
360 CloseHandle(wmcda->handle);
365 /**************************************************************************
366 * MCICDA_GetDevCaps [internal]
368 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
369 LPMCI_GETDEVCAPS_PARMS lpParms)
373 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
375 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
377 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
378 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
380 switch (lpParms->dwItem) {
381 case MCI_GETDEVCAPS_CAN_RECORD:
382 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
383 ret = MCI_RESOURCE_RETURNED;
385 case MCI_GETDEVCAPS_HAS_AUDIO:
386 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
387 ret = MCI_RESOURCE_RETURNED;
389 case MCI_GETDEVCAPS_HAS_VIDEO:
390 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
391 ret = MCI_RESOURCE_RETURNED;
393 case MCI_GETDEVCAPS_DEVICE_TYPE:
394 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
395 ret = MCI_RESOURCE_RETURNED;
397 case MCI_GETDEVCAPS_USES_FILES:
398 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
399 ret = MCI_RESOURCE_RETURNED;
401 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
402 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
403 ret = MCI_RESOURCE_RETURNED;
405 case MCI_GETDEVCAPS_CAN_EJECT:
406 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
407 ret = MCI_RESOURCE_RETURNED;
409 case MCI_GETDEVCAPS_CAN_PLAY:
410 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
411 ret = MCI_RESOURCE_RETURNED;
413 case MCI_GETDEVCAPS_CAN_SAVE:
414 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
415 ret = MCI_RESOURCE_RETURNED;
418 ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
419 return MCIERR_UNRECOGNIZED_COMMAND;
422 TRACE("No GetDevCaps-Item !\n");
423 return MCIERR_UNRECOGNIZED_COMMAND;
425 TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
429 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
431 unsigned long serial = 0;
434 DWORD dwStart, dwEnd;
437 * wMagic collects the wFrames from track 1
438 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
440 * There it is collected for correcting the serial when there are less than
443 wMagic = toc->TrackData[0].Address[3];
444 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
446 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
447 serial += (toc->TrackData[i].Address[1] << 16) |
448 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
450 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
452 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
453 serial += wMagic + (dwEnd - dwStart);
459 /**************************************************************************
460 * MCICDA_Info [internal]
462 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
465 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
469 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
471 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
472 return MCIERR_NULL_PARAMETER_BLOCK;
473 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
475 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
477 if (dwFlags & MCI_INFO_PRODUCT) {
478 str = "Wine's audio CD";
479 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
480 ret = MCIERR_NO_IDENTITY;
481 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
486 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
487 &toc, sizeof(toc), &br, NULL)) {
488 return MCICDA_GetError(wmcda);
491 res = CDROM_Audio_GetSerial(&toc);
492 sprintf(buffer, "%lu", res);
495 WARN("Don't know this info command (%lu)\n", dwFlags);
496 ret = MCIERR_UNRECOGNIZED_COMMAND;
499 if (lpParms->dwRetSize <= strlen(str)) {
500 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
501 ret = MCIERR_PARAM_OVERFLOW;
503 strcpy(lpParms->lpstrReturn, str);
506 *lpParms->lpstrReturn = 0;
508 TRACE("=> %s (%ld)\n", lpParms->lpstrReturn, ret);
512 /**************************************************************************
513 * MCICDA_Status [internal]
515 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
517 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
520 CDROM_SUB_Q_DATA_FORMAT fmt;
521 SUB_Q_CHANNEL_DATA data;
525 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
527 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
528 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
530 if (dwFlags & MCI_NOTIFY) {
531 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
532 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
533 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
535 if (dwFlags & MCI_STATUS_ITEM) {
536 TRACE("dwItem = %lx\n", lpParms->dwItem);
537 switch (lpParms->dwItem) {
538 case MCI_STATUS_CURRENT_TRACK:
539 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
540 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
541 &data, sizeof(data), &br, NULL))
543 return MCICDA_GetError(wmcda);
545 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
546 TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
548 case MCI_STATUS_LENGTH:
549 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
550 &toc, sizeof(toc), &br, NULL)) {
551 WARN("error reading TOC !\n");
552 return MCICDA_GetError(wmcda);
554 if (dwFlags & MCI_TRACK) {
555 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
556 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
557 return MCIERR_OUTOFRANGE;
558 idx = lpParms->dwTrack - toc.FirstTrack;
559 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
560 FRAME_OF_TOC(toc, lpParms->dwTrack);
561 /* Windows returns one frame less than the total track length for the
562 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
563 if (lpParms->dwTrack == toc.LastTrack)
566 /* Sum of the lengths of all of the tracks. Inherits the
567 'off by one frame' behavior from the length of the last track.
568 See above comment. */
569 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
570 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
572 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
573 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
574 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
577 TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
579 case MCI_STATUS_MODE:
580 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
581 TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
582 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
583 ret = MCI_RESOURCE_RETURNED;
585 case MCI_STATUS_MEDIA_PRESENT:
586 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
587 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
588 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
589 ret = MCI_RESOURCE_RETURNED;
591 case MCI_STATUS_NUMBER_OF_TRACKS:
592 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
593 &toc, sizeof(toc), &br, NULL)) {
594 WARN("error reading TOC !\n");
595 return MCICDA_GetError(wmcda);
597 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
598 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
599 if (lpParms->dwReturn == (WORD)-1)
600 return MCICDA_GetError(wmcda);
602 case MCI_STATUS_POSITION:
603 if (dwFlags & MCI_STATUS_START) {
604 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
605 &toc, sizeof(toc), &br, NULL)) {
606 WARN("error reading TOC !\n");
607 return MCICDA_GetError(wmcda);
609 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
610 TRACE("get MCI_STATUS_START !\n");
611 } else if (dwFlags & MCI_TRACK) {
612 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
613 &toc, sizeof(toc), &br, NULL)) {
614 WARN("error reading TOC !\n");
615 return MCICDA_GetError(wmcda);
617 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
618 return MCIERR_OUTOFRANGE;
619 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
620 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
622 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
623 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
624 &data, sizeof(data), &br, NULL)) {
625 return MCICDA_GetError(wmcda);
627 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
629 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
630 TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
632 case MCI_STATUS_READY:
633 TRACE("MCI_STATUS_READY !\n");
634 switch (MCICDA_GetStatus(wmcda))
636 case MCI_MODE_NOT_READY:
638 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
641 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
644 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
645 ret = MCI_RESOURCE_RETURNED;
647 case MCI_STATUS_TIME_FORMAT:
648 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, wmcda->dwTimeFormat);
649 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
650 ret = MCI_RESOURCE_RETURNED;
652 case 4001: /* FIXME: for bogus FullCD */
653 case MCI_CDA_STATUS_TYPE_TRACK:
654 if (!(dwFlags & MCI_TRACK))
655 ret = MCIERR_MISSING_PARAMETER;
657 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
658 &toc, sizeof(toc), &br, NULL)) {
659 WARN("error reading TOC !\n");
660 return MCICDA_GetError(wmcda);
662 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
663 ret = MCIERR_OUTOFRANGE;
665 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
666 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
668 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
671 FIXME("unknown command %08lX !\n", lpParms->dwItem);
672 return MCIERR_UNRECOGNIZED_COMMAND;
675 WARN("not MCI_STATUS_ITEM !\n");
680 /**************************************************************************
681 * MCICDA_Play [internal]
683 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
685 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
686 DWORD ret = 0, start, end;
689 CDROM_PLAY_AUDIO_MSF play;
690 CDROM_SUB_Q_DATA_FORMAT fmt;
691 SUB_Q_CHANNEL_DATA data;
693 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
696 return MCIERR_NULL_PARAMETER_BLOCK;
699 return MCIERR_INVALID_DEVICE_ID;
701 if (dwFlags & MCI_FROM) {
702 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
703 TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
705 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
706 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
707 &data, sizeof(data), &br, NULL)) {
708 return MCICDA_GetError(wmcda);
710 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
712 if (dwFlags & MCI_TO) {
713 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
714 TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
716 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
717 &toc, sizeof(toc), &br, NULL)) {
718 WARN("error reading TOC !\n");
719 return MCICDA_GetError(wmcda);
721 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
723 TRACE("Playing from %lu to %lu\n", start, end);
724 play.StartingM = start / CDFRAMES_PERMIN;
725 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
726 play.StartingF = start % CDFRAMES_PERSEC;
727 play.EndingM = end / CDFRAMES_PERMIN;
728 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
729 play.EndingF = end % CDFRAMES_PERSEC;
730 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
731 NULL, 0, &br, NULL)) {
732 ret = MCIERR_HARDWARE;
733 } else if (dwFlags & MCI_NOTIFY) {
734 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
736 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
737 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
743 /**************************************************************************
744 * MCICDA_Stop [internal]
746 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
748 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
751 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
753 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
755 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
756 return MCIERR_HARDWARE;
758 if (lpParms && (dwFlags & MCI_NOTIFY)) {
759 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
760 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
761 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
766 /**************************************************************************
767 * MCICDA_Pause [internal]
769 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
771 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
774 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
776 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
778 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
779 return MCIERR_HARDWARE;
781 if (lpParms && (dwFlags & MCI_NOTIFY)) {
782 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
783 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
784 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
789 /**************************************************************************
790 * MCICDA_Resume [internal]
792 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
794 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
797 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
799 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
801 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
802 return MCIERR_HARDWARE;
804 if (lpParms && (dwFlags & MCI_NOTIFY)) {
805 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
806 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
807 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
812 /**************************************************************************
813 * MCICDA_Seek [internal]
815 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
818 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
819 CDROM_SEEK_AUDIO_MSF seek;
823 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
825 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
826 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
828 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
829 case MCI_SEEK_TO_START:
830 TRACE("Seeking to start\n");
831 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
832 &toc, sizeof(toc), &br, NULL)) {
833 WARN("error reading TOC !\n");
834 return MCICDA_GetError(wmcda);
836 at = FRAME_OF_TOC(toc, toc.FirstTrack);
838 case MCI_SEEK_TO_END:
839 TRACE("Seeking to end\n");
840 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
841 &toc, sizeof(toc), &br, NULL)) {
842 WARN("error reading TOC !\n");
843 return MCICDA_GetError(wmcda);
845 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
848 TRACE("Seeking to %lu\n", lpParms->dwTo);
852 TRACE("Unknown seek action %08lX\n",
853 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
854 return MCIERR_UNSUPPORTED_FUNCTION;
856 seek.M = at / CDFRAMES_PERMIN;
857 seek.S = (at / CDFRAMES_PERSEC) % 60;
858 seek.F = at % CDFRAMES_PERSEC;
859 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
861 return MCIERR_HARDWARE;
863 if (dwFlags & MCI_NOTIFY) {
864 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
865 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
866 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
871 /**************************************************************************
872 * MCICDA_SetDoor [internal]
874 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
876 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
879 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
881 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
883 if (!DeviceIoControl(wmcda->handle,
884 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
885 NULL, 0, NULL, 0, &br, NULL))
886 return MCIERR_HARDWARE;
891 /**************************************************************************
892 * MCICDA_Set [internal]
894 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
896 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
898 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
900 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
902 if (dwFlags & MCI_SET_DOOR_OPEN) {
903 MCICDA_SetDoor(wDevID, TRUE);
905 if (dwFlags & MCI_SET_DOOR_CLOSED) {
906 MCICDA_SetDoor(wDevID, FALSE);
909 /* only functions which require valid lpParms below this line ! */
910 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
912 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
913 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
915 if (dwFlags & MCI_SET_TIME_FORMAT) {
916 switch (lpParms->dwTimeFormat) {
917 case MCI_FORMAT_MILLISECONDS:
918 TRACE("MCI_FORMAT_MILLISECONDS !\n");
921 TRACE("MCI_FORMAT_MSF !\n");
923 case MCI_FORMAT_TMSF:
924 TRACE("MCI_FORMAT_TMSF !\n");
927 WARN("bad time format !\n");
928 return MCIERR_BAD_TIME_FORMAT;
930 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
932 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
933 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
934 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
935 if (dwFlags & MCI_NOTIFY) {
936 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
937 lpParms->dwCallback);
938 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
939 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
944 /**************************************************************************
945 * DriverProc (MCICDA.@)
947 LONG CALLBACK MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
948 DWORD dwParam1, DWORD dwParam2)
951 case DRV_LOAD: return 1;
952 case DRV_FREE: return 1;
953 case DRV_OPEN: return MCICDA_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
954 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
955 case DRV_ENABLE: return 1;
956 case DRV_DISABLE: return 1;
957 case DRV_QUERYCONFIGURE: return 1;
958 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
959 case DRV_INSTALL: return DRVCNF_RESTART;
960 case DRV_REMOVE: return DRVCNF_RESTART;
963 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
966 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
967 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
968 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
969 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSA)dwParam2);
970 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
971 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
972 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
973 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
974 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
975 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
976 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
977 /* FIXME: I wonder if those two next items are really called ? */
978 case MCI_SET_DOOR_OPEN: FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
979 return MCICDA_SetDoor(dwDevID, TRUE);
980 case MCI_SET_DOOR_CLOSED: FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
981 return MCICDA_SetDoor(dwDevID, FALSE);
982 /* commands that should be supported */
998 FIXME("Unsupported yet command [%lu]\n", wMsg);
1000 /* commands that should report an error */
1002 TRACE("Unsupported command [%lu]\n", wMsg);
1006 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1009 TRACE("Sending msg [%lu] to default driver proc\n", wMsg);
1010 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1012 return MCIERR_UNRECOGNIZED_COMMAND;
1015 /*-----------------------------------------------------------------------*/