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
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
40 #define CDFRAMES_PERSEC 75
41 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
42 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
43 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
47 int nUseCount; /* Incremented for each shared open */
48 BOOL fShareable; /* TRUE if first open was shareable */
49 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
50 HANDLE hCallback; /* Callback handle for pending notification */
55 /*-----------------------------------------------------------------------*/
57 /**************************************************************************
58 * MCICDA_drvOpen [internal]
60 static DWORD MCICDA_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
62 WINE_MCICDAUDIO* wmcda;
64 if (!modp) return 0xFFFFFFFF;
66 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
71 wmcda->wDevID = modp->wDeviceID;
72 mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
73 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
74 modp->wType = MCI_DEVTYPE_CD_AUDIO;
75 return modp->wDeviceID;
78 /**************************************************************************
79 * MCICDA_drvClose [internal]
81 static DWORD MCICDA_drvClose(DWORD dwDevID)
83 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
86 HeapFree(GetProcessHeap(), 0, wmcda);
87 mciSetDriverData(dwDevID, 0);
89 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
92 /**************************************************************************
93 * MCICDA_GetOpenDrv [internal]
95 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
97 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
99 if (wmcda == NULL || wmcda->nUseCount == 0) {
100 WARN("Invalid wDevID=%u\n", wDevID);
106 /**************************************************************************
107 * MCICDA_GetStatus [internal]
109 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
111 CDROM_SUB_Q_DATA_FORMAT fmt;
112 SUB_Q_CHANNEL_DATA data;
114 DWORD mode = MCI_MODE_NOT_READY;
116 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
117 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
118 &data, sizeof(data), &br, NULL)) {
119 if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
121 switch (data.CurrentPosition.Header.AudioStatus)
123 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
124 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
125 case AUDIO_STATUS_NO_STATUS:
126 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
127 case AUDIO_STATUS_PLAY_ERROR:
128 case AUDIO_STATUS_NOT_SUPPORTED:
136 /**************************************************************************
137 * MCICDA_GetError [internal]
139 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
141 switch (GetLastError())
143 case STATUS_NO_MEDIA_IN_DEVICE: return MCIERR_DEVICE_NOT_READY;
144 case STATUS_IO_DEVICE_ERROR: return MCIERR_HARDWARE;
146 FIXME("Unknown mode %lx\n", GetLastError());
148 return MCIERR_DRIVER_INTERNAL;
151 /**************************************************************************
152 * MCICDA_CalcFrame [internal]
154 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
162 TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
164 switch (wmcda->dwTimeFormat) {
165 case MCI_FORMAT_MILLISECONDS:
166 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
167 TRACE("MILLISECONDS %lu\n", dwFrame);
170 TRACE("MSF %02u:%02u:%02u\n",
171 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
172 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
173 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
174 dwFrame += MCI_MSF_FRAME(dwTime);
176 case MCI_FORMAT_TMSF:
177 default: /* unknown format ! force TMSF ! ... */
178 wTrack = MCI_TMSF_TRACK(dwTime);
179 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
180 &toc, sizeof(toc), &br, NULL))
182 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
184 TRACE("MSF %02u-%02u:%02u:%02u\n",
185 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
186 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
187 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
188 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
189 wTrack, addr[0], addr[1], addr[2]);
190 dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
191 CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
192 addr[2] + MCI_TMSF_FRAME(dwTime);
198 /**************************************************************************
199 * MCICDA_CalcTime [internal]
201 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
211 TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
214 case MCI_FORMAT_MILLISECONDS:
215 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
216 TRACE("MILLISECONDS %lu\n", dwTime);
220 wMinutes = dwFrame / CDFRAMES_PERMIN;
221 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
222 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
223 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
224 TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
225 wMinutes, wSeconds, wFrames, dwTime);
226 *lpRet = MCI_COLONIZED3_RETURN;
228 case MCI_FORMAT_TMSF:
229 default: /* unknown format ! force TMSF ! ... */
230 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
231 &toc, sizeof(toc), &br, NULL))
233 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
234 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
235 ERR("Out of range value %lu [%u,%u]\n",
236 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
237 FRAME_OF_TOC(toc, toc.LastTrack + 1));
241 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
242 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
246 dwFrame -= FRAME_OF_TOC(toc, wTrack);
247 wMinutes = dwFrame / CDFRAMES_PERMIN;
248 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
249 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
250 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
251 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
252 *lpRet = MCI_COLONIZED4_RETURN;
258 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
259 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
261 /**************************************************************************
262 * MCICDA_Open [internal]
264 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
267 DWORD ret = MCIERR_HARDWARE;
268 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
273 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
275 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
276 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
278 dwDeviceID = lpOpenParms->wDeviceID;
280 if (wmcda->nUseCount > 0) {
281 /* The driver is already open on this channel */
282 /* If the driver was opened shareable before and this open specifies */
283 /* shareable then increment the use count */
284 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
287 return MCIERR_MUST_USE_SHAREABLE;
289 wmcda->nUseCount = 1;
290 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
292 if (dwFlags & MCI_OPEN_ELEMENT) {
293 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
294 WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
295 return MCIERR_NO_ELEMENT_ALLOWED;
297 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
298 lpOpenParms->lpstrElementName[2])
300 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms->lpstrElementName);
301 ret = MCIERR_NO_ELEMENT_ALLOWED;
304 drive = toupper(lpOpenParms->lpstrElementName[0]);
305 strcpy(root, "A:\\");
307 if (GetDriveTypeA(root) != DRIVE_CDROM)
309 ret = MCIERR_INVALID_DEVICE_NAME;
315 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
316 strcpy(root, "A:\\");
317 for (count = 0; root[0] <= 'Z'; root[0]++)
319 if (GetDriveTypeA(root) == DRIVE_CDROM && ++count >= dwDeviceID)
327 ret = MCIERR_INVALID_DEVICE_ID;
332 wmcda->wNotifyDeviceID = dwDeviceID;
333 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
335 /* now, open the handle */
336 strcpy(root, "\\\\.\\A:");
338 wmcda->handle = CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
339 if (wmcda->handle != INVALID_HANDLE_VALUE)
347 /**************************************************************************
348 * MCICDA_Close [internal]
350 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
352 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
354 TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
356 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
358 if (--wmcda->nUseCount == 0) {
359 CloseHandle(wmcda->handle);
364 /**************************************************************************
365 * MCICDA_GetDevCaps [internal]
367 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
368 LPMCI_GETDEVCAPS_PARMS lpParms)
372 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
374 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
376 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
377 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
379 switch (lpParms->dwItem) {
380 case MCI_GETDEVCAPS_CAN_RECORD:
381 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
382 ret = MCI_RESOURCE_RETURNED;
384 case MCI_GETDEVCAPS_HAS_AUDIO:
385 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
386 ret = MCI_RESOURCE_RETURNED;
388 case MCI_GETDEVCAPS_HAS_VIDEO:
389 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
390 ret = MCI_RESOURCE_RETURNED;
392 case MCI_GETDEVCAPS_DEVICE_TYPE:
393 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
394 ret = MCI_RESOURCE_RETURNED;
396 case MCI_GETDEVCAPS_USES_FILES:
397 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
398 ret = MCI_RESOURCE_RETURNED;
400 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
401 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
402 ret = MCI_RESOURCE_RETURNED;
404 case MCI_GETDEVCAPS_CAN_EJECT:
405 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
406 ret = MCI_RESOURCE_RETURNED;
408 case MCI_GETDEVCAPS_CAN_PLAY:
409 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
410 ret = MCI_RESOURCE_RETURNED;
412 case MCI_GETDEVCAPS_CAN_SAVE:
413 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
414 ret = MCI_RESOURCE_RETURNED;
417 ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
418 return MCIERR_UNRECOGNIZED_COMMAND;
421 TRACE("No GetDevCaps-Item !\n");
422 return MCIERR_UNRECOGNIZED_COMMAND;
424 TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
428 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
430 unsigned long serial = 0;
433 DWORD dwStart, dwEnd;
436 * wMagic collects the wFrames from track 1
437 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
439 * There it is collected for correcting the serial when there are less than
442 wMagic = toc->TrackData[0].Address[3];
443 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
445 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
446 serial += (toc->TrackData[i].Address[1] << 16) |
447 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
449 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
451 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
452 serial += wMagic + (dwEnd - dwStart);
458 /**************************************************************************
459 * MCICDA_Info [internal]
461 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
464 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
468 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
470 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
471 return MCIERR_NULL_PARAMETER_BLOCK;
472 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
474 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
476 if (dwFlags & MCI_INFO_PRODUCT) {
477 str = "Wine's audio CD";
478 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
479 ret = MCIERR_NO_IDENTITY;
480 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
485 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
486 &toc, sizeof(toc), &br, NULL)) {
487 return MCICDA_GetError(wmcda);
490 res = CDROM_Audio_GetSerial(&toc);
491 sprintf(buffer, "%lu", res);
494 WARN("Don't know this info command (%lu)\n", dwFlags);
495 ret = MCIERR_UNRECOGNIZED_COMMAND;
498 if (lpParms->dwRetSize <= strlen(str)) {
499 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
500 ret = MCIERR_PARAM_OVERFLOW;
502 strcpy(lpParms->lpstrReturn, str);
505 *lpParms->lpstrReturn = 0;
507 TRACE("=> %s (%ld)\n", lpParms->lpstrReturn, ret);
511 /**************************************************************************
512 * MCICDA_Status [internal]
514 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
516 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
519 CDROM_SUB_Q_DATA_FORMAT fmt;
520 SUB_Q_CHANNEL_DATA data;
524 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
526 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
527 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
529 if (dwFlags & MCI_NOTIFY) {
530 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
531 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
532 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
534 if (dwFlags & MCI_STATUS_ITEM) {
535 TRACE("dwItem = %lx\n", lpParms->dwItem);
536 switch (lpParms->dwItem) {
537 case MCI_STATUS_CURRENT_TRACK:
538 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
539 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
540 &data, sizeof(data), &br, NULL))
542 return MCICDA_GetError(wmcda);
544 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
545 TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
547 case MCI_STATUS_LENGTH:
548 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
549 &toc, sizeof(toc), &br, NULL)) {
550 WARN("error reading TOC !\n");
551 return MCICDA_GetError(wmcda);
553 if (dwFlags & MCI_TRACK) {
554 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
555 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
556 return MCIERR_OUTOFRANGE;
557 idx = lpParms->dwTrack - toc.FirstTrack;
558 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
559 FRAME_OF_TOC(toc, lpParms->dwTrack);
560 /* Windows returns one frame less than the total track length for the
561 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
562 if (lpParms->dwTrack == toc.LastTrack)
565 /* Sum of the lengths of all of the tracks. Inherits the
566 'off by one frame' behavior from the length of the last track.
567 See above comment. */
568 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
569 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
571 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
572 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
573 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
576 TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
578 case MCI_STATUS_MODE:
579 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
580 TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
581 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
582 ret = MCI_RESOURCE_RETURNED;
584 case MCI_STATUS_MEDIA_PRESENT:
585 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
586 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
587 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
588 ret = MCI_RESOURCE_RETURNED;
590 case MCI_STATUS_NUMBER_OF_TRACKS:
591 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
592 &toc, sizeof(toc), &br, NULL)) {
593 WARN("error reading TOC !\n");
594 return MCICDA_GetError(wmcda);
596 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
597 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
598 if (lpParms->dwReturn == (WORD)-1)
599 return MCICDA_GetError(wmcda);
601 case MCI_STATUS_POSITION:
602 if (dwFlags & MCI_STATUS_START) {
603 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
604 &toc, sizeof(toc), &br, NULL)) {
605 WARN("error reading TOC !\n");
606 return MCICDA_GetError(wmcda);
608 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
609 TRACE("get MCI_STATUS_START !\n");
610 } else if (dwFlags & MCI_TRACK) {
611 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
612 &toc, sizeof(toc), &br, NULL)) {
613 WARN("error reading TOC !\n");
614 return MCICDA_GetError(wmcda);
616 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
617 return MCIERR_OUTOFRANGE;
618 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
619 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
621 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
622 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
623 &data, sizeof(data), &br, NULL)) {
624 return MCICDA_GetError(wmcda);
626 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
628 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
629 TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
631 case MCI_STATUS_READY:
632 TRACE("MCI_STATUS_READY !\n");
633 switch (MCICDA_GetStatus(wmcda))
635 case MCI_MODE_NOT_READY:
637 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
640 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
643 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
644 ret = MCI_RESOURCE_RETURNED;
646 case MCI_STATUS_TIME_FORMAT:
647 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, wmcda->dwTimeFormat);
648 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
649 ret = MCI_RESOURCE_RETURNED;
651 case 4001: /* FIXME: for bogus FullCD */
652 case MCI_CDA_STATUS_TYPE_TRACK:
653 if (!(dwFlags & MCI_TRACK))
654 ret = MCIERR_MISSING_PARAMETER;
656 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
657 &toc, sizeof(toc), &br, NULL)) {
658 WARN("error reading TOC !\n");
659 return MCICDA_GetError(wmcda);
661 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
662 ret = MCIERR_OUTOFRANGE;
664 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
665 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
667 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
670 FIXME("unknown command %08lX !\n", lpParms->dwItem);
671 return MCIERR_UNRECOGNIZED_COMMAND;
674 WARN("not MCI_STATUS_ITEM !\n");
679 /**************************************************************************
680 * MCICDA_Play [internal]
682 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
684 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
685 DWORD ret = 0, start, end;
688 CDROM_PLAY_AUDIO_MSF play;
689 CDROM_SUB_Q_DATA_FORMAT fmt;
690 SUB_Q_CHANNEL_DATA data;
692 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
695 return MCIERR_NULL_PARAMETER_BLOCK;
698 return MCIERR_INVALID_DEVICE_ID;
700 if (dwFlags & MCI_FROM) {
701 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
702 TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
704 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
705 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
706 &data, sizeof(data), &br, NULL)) {
707 return MCICDA_GetError(wmcda);
709 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
711 if (dwFlags & MCI_TO) {
712 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
713 TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
715 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
716 &toc, sizeof(toc), &br, NULL)) {
717 WARN("error reading TOC !\n");
718 return MCICDA_GetError(wmcda);
720 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
722 TRACE("Playing from %lu to %lu\n", start, end);
723 play.StartingM = start / CDFRAMES_PERMIN;
724 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
725 play.StartingF = start % CDFRAMES_PERSEC;
726 play.EndingM = end / CDFRAMES_PERMIN;
727 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
728 play.EndingF = end % CDFRAMES_PERSEC;
729 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
730 NULL, 0, &br, NULL)) {
731 ret = MCIERR_HARDWARE;
732 } else if (dwFlags & MCI_NOTIFY) {
733 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
735 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
736 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
742 /**************************************************************************
743 * MCICDA_Stop [internal]
745 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
747 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
750 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
752 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
754 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
755 return MCIERR_HARDWARE;
757 if (lpParms && (dwFlags & MCI_NOTIFY)) {
758 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
759 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
760 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
765 /**************************************************************************
766 * MCICDA_Pause [internal]
768 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
770 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
773 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
775 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
777 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
778 return MCIERR_HARDWARE;
780 if (lpParms && (dwFlags & MCI_NOTIFY)) {
781 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
782 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
783 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
788 /**************************************************************************
789 * MCICDA_Resume [internal]
791 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
793 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
796 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
798 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
800 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
801 return MCIERR_HARDWARE;
803 if (lpParms && (dwFlags & MCI_NOTIFY)) {
804 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
805 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
806 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
811 /**************************************************************************
812 * MCICDA_Seek [internal]
814 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
817 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
818 CDROM_SEEK_AUDIO_MSF seek;
822 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
824 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
825 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
827 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
828 case MCI_SEEK_TO_START:
829 TRACE("Seeking to start\n");
830 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
831 &toc, sizeof(toc), &br, NULL)) {
832 WARN("error reading TOC !\n");
833 return MCICDA_GetError(wmcda);
835 at = FRAME_OF_TOC(toc, toc.FirstTrack);
837 case MCI_SEEK_TO_END:
838 TRACE("Seeking to end\n");
839 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
840 &toc, sizeof(toc), &br, NULL)) {
841 WARN("error reading TOC !\n");
842 return MCICDA_GetError(wmcda);
844 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
847 TRACE("Seeking to %lu\n", lpParms->dwTo);
851 TRACE("Unknown seek action %08lX\n",
852 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
853 return MCIERR_UNSUPPORTED_FUNCTION;
855 seek.M = at / CDFRAMES_PERMIN;
856 seek.S = (at / CDFRAMES_PERSEC) % 60;
857 seek.F = at % CDFRAMES_PERSEC;
858 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
860 return MCIERR_HARDWARE;
862 if (dwFlags & MCI_NOTIFY) {
863 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
864 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
865 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
870 /**************************************************************************
871 * MCICDA_SetDoor [internal]
873 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
875 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
878 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
880 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
882 if (!DeviceIoControl(wmcda->handle,
883 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
884 NULL, 0, NULL, 0, &br, NULL))
885 return MCIERR_HARDWARE;
890 /**************************************************************************
891 * MCICDA_Set [internal]
893 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
895 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
897 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
899 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
901 if (dwFlags & MCI_SET_DOOR_OPEN) {
902 MCICDA_SetDoor(wDevID, TRUE);
904 if (dwFlags & MCI_SET_DOOR_CLOSED) {
905 MCICDA_SetDoor(wDevID, FALSE);
908 /* only functions which require valid lpParms below this line ! */
909 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
911 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
912 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
914 if (dwFlags & MCI_SET_TIME_FORMAT) {
915 switch (lpParms->dwTimeFormat) {
916 case MCI_FORMAT_MILLISECONDS:
917 TRACE("MCI_FORMAT_MILLISECONDS !\n");
920 TRACE("MCI_FORMAT_MSF !\n");
922 case MCI_FORMAT_TMSF:
923 TRACE("MCI_FORMAT_TMSF !\n");
926 WARN("bad time format !\n");
927 return MCIERR_BAD_TIME_FORMAT;
929 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
931 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
932 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
933 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
934 if (dwFlags & MCI_NOTIFY) {
935 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
936 lpParms->dwCallback);
937 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
938 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
943 /**************************************************************************
944 * DriverProc (MCICDA.@)
946 LONG CALLBACK MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
947 DWORD dwParam1, DWORD dwParam2)
950 case DRV_LOAD: return 1;
951 case DRV_FREE: return 1;
952 case DRV_OPEN: return MCICDA_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
953 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
954 case DRV_ENABLE: return 1;
955 case DRV_DISABLE: return 1;
956 case DRV_QUERYCONFIGURE: return 1;
957 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
958 case DRV_INSTALL: return DRVCNF_RESTART;
959 case DRV_REMOVE: return DRVCNF_RESTART;
962 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
965 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
966 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
967 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
968 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSA)dwParam2);
969 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
970 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
971 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
972 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
973 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
974 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
975 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
976 /* FIXME: I wonder if those two next items are really called ? */
977 case MCI_SET_DOOR_OPEN: FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
978 return MCICDA_SetDoor(dwDevID, TRUE);
979 case MCI_SET_DOOR_CLOSED: FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
980 return MCICDA_SetDoor(dwDevID, FALSE);
981 /* commands that should be supported */
997 FIXME("Unsupported yet command [%lu]\n", wMsg);
999 /* commands that should report an error */
1001 TRACE("Unsupported command [%lu]\n", wMsg);
1005 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1008 TRACE("Sending msg [%lu] to default driver proc\n", wMsg);
1009 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1011 return MCIERR_UNRECOGNIZED_COMMAND;
1014 /*-----------------------------------------------------------------------*/