Cleaned up a bit, removed todos for OpenThread, avoid TerminateThread
[wine] / dlls / winmm / mcicda / mcicda.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * MCI driver for audio CD (MCICDA)
4  *
5  * Copyright 1994    Martin Ayotte
6  * Copyright 1998-99 Eric Pouech
7  * Copyright 2000    Andreas Mohr
8  *
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.
13  *
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.
18  *
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
22  */
23
24 #include "config.h"
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "mmddk.h"
33 #include "winioctl.h"
34 #include "ntddstor.h"
35 #include "ntddcdrm.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
39
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)
44
45 typedef struct {
46     UINT                wDevID;
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 */
51     DWORD               dwTimeFormat;
52     HANDLE              handle;
53 } WINE_MCICDAUDIO;
54
55 /*-----------------------------------------------------------------------*/
56
57 /**************************************************************************
58  *                              MCICDA_drvOpen                  [internal]      
59  */
60 static  DWORD   MCICDA_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
61 {
62     WINE_MCICDAUDIO*    wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  sizeof(WINE_MCICDAUDIO));
63
64     if (!wmcda)
65         return 0;
66
67     wmcda->wDevID = modp->wDeviceID;
68     mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
69     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
70     modp->wType = MCI_DEVTYPE_CD_AUDIO;
71     return modp->wDeviceID;
72 }
73
74 /**************************************************************************
75  *                              MCICDA_drvClose                 [internal]      
76  */
77 static  DWORD   MCICDA_drvClose(DWORD dwDevID)
78 {
79     WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
80
81     if (wmcda) {
82         HeapFree(GetProcessHeap(), 0, wmcda);
83         mciSetDriverData(dwDevID, 0);
84     }
85     return 0;
86 }
87
88 /**************************************************************************
89  *                              MCICDA_GetOpenDrv               [internal]      
90  */
91 static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
92 {
93     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
94     
95     if (wmcda == NULL || wmcda->nUseCount == 0) {
96         WARN("Invalid wDevID=%u\n", wDevID);
97         return 0;
98     }
99     return wmcda;
100 }
101
102 /**************************************************************************
103  *                              MCICDA_GetStatus                [internal]
104  */
105 static  DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
106 {
107     CDROM_SUB_Q_DATA_FORMAT     fmt;
108     SUB_Q_CHANNEL_DATA          data;
109     DWORD                       br;
110     DWORD                       mode = MCI_MODE_NOT_READY;
111
112     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
113     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 
114                          &data, sizeof(data), &br, NULL)) {
115         if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
116     } else {
117         switch (data.CurrentPosition.Header.AudioStatus)
118         {
119         case AUDIO_STATUS_IN_PROGRESS:          mode = MCI_MODE_PLAY;   break;
120         case AUDIO_STATUS_PAUSED:               mode = MCI_MODE_PAUSE;  break;
121         case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
122         case AUDIO_STATUS_PLAY_ERROR:
123         case AUDIO_STATUS_NOT_SUPPORTED:
124         case AUDIO_STATUS_NO_STATUS:
125         default:
126             break;
127         }
128     }
129     return mode;
130 }
131
132 /**************************************************************************
133  *                              MCICDA_GetError                 [internal]
134  */
135 static  int     MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
136 {
137     switch (GetLastError())
138     {
139     case STATUS_NO_MEDIA_IN_DEVICE:     return MCIERR_DEVICE_NOT_READY;
140     case STATUS_IO_DEVICE_ERROR:        return MCIERR_HARDWARE;
141     default:
142         FIXME("Unknown mode %lx\n", GetLastError());
143     }
144     return MCIERR_DRIVER_INTERNAL;
145 }
146
147 /**************************************************************************
148  *                      MCICDA_CalcFrame                        [internal]
149  */
150 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
151 {
152     DWORD       dwFrame = 0;
153     UINT        wTrack;
154     CDROM_TOC   toc;
155     DWORD       br;
156     BYTE*       addr;
157     
158     TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
159     
160     switch (wmcda->dwTimeFormat) {
161     case MCI_FORMAT_MILLISECONDS:
162         dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
163         TRACE("MILLISECONDS %lu\n", dwFrame);
164         break;
165     case MCI_FORMAT_MSF:
166         TRACE("MSF %02u:%02u:%02u\n",
167               MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
168         dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
169         dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
170         dwFrame += MCI_MSF_FRAME(dwTime);
171         break;
172     case MCI_FORMAT_TMSF:
173     default: /* unknown format ! force TMSF ! ... */
174         wTrack = MCI_TMSF_TRACK(dwTime);
175         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
176                              &toc, sizeof(toc), &br, NULL))
177             return 0;
178         if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
179             return 0;
180         TRACE("MSF %02u-%02u:%02u:%02u\n",
181               MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), 
182               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
183         addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
184         TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
185               wTrack, addr[0], addr[1], addr[2]);
186         dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
187             CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
188             addr[2] + MCI_TMSF_FRAME(dwTime);
189         break;
190     }
191     return dwFrame;
192 }
193
194 /**************************************************************************
195  *                      MCICDA_CalcTime                         [internal]
196  */
197 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
198 {
199     DWORD       dwTime = 0;
200     UINT        wTrack;
201     UINT        wMinutes;
202     UINT        wSeconds;
203     UINT        wFrames;
204     CDROM_TOC   toc;
205     DWORD       br;
206     
207     TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
208     
209     switch (tf) {
210     case MCI_FORMAT_MILLISECONDS:
211         dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
212         TRACE("MILLISECONDS %lu\n", dwTime);
213         *lpRet = 0;
214         break;
215     case MCI_FORMAT_MSF:
216         wMinutes = dwFrame / CDFRAMES_PERMIN;
217         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
218         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
219         dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
220         TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
221               wMinutes, wSeconds, wFrames, dwTime);
222         *lpRet = MCI_COLONIZED3_RETURN;
223         break;
224     case MCI_FORMAT_TMSF:
225     default:    /* unknown format ! force TMSF ! ... */
226         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
227                              &toc, sizeof(toc), &br, NULL))
228             return 0;
229         if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) || 
230             dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
231             ERR("Out of range value %lu [%u,%u]\n", 
232                 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack), 
233                 FRAME_OF_TOC(toc, toc.LastTrack + 1));
234             *lpRet = 0;
235             return 0;
236         }
237         for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
238             if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
239                 break;
240         }
241         wTrack--;
242         dwFrame -= FRAME_OF_TOC(toc, wTrack);
243         wMinutes = dwFrame / CDFRAMES_PERMIN;
244         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
245         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
246         dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
247         TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
248         *lpRet = MCI_COLONIZED4_RETURN;
249         break;
250     }
251     return dwTime;
252 }
253
254 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
255 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
256
257 /**************************************************************************
258  *                              MCICDA_Open                     [internal]
259  */
260 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
261 {
262     DWORD               dwDeviceID;
263     DWORD               ret = MCIERR_HARDWARE;
264     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
265     char                root[7];
266     int                 count;
267     char                drive = 0;
268
269     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
270     
271     if (lpOpenParms == NULL)            return MCIERR_NULL_PARAMETER_BLOCK;
272     if (wmcda == NULL)                  return MCIERR_INVALID_DEVICE_ID;
273
274     dwDeviceID = lpOpenParms->wDeviceID;
275
276     if (wmcda->nUseCount > 0) {
277         /* The driver is already open on this channel */
278         /* If the driver was opened shareable before and this open specifies */
279         /* shareable then increment the use count */
280         if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
281             ++wmcda->nUseCount;
282         else
283             return MCIERR_MUST_USE_SHAREABLE;
284     } else {
285         wmcda->nUseCount = 1;
286         wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
287     }
288     if (dwFlags & MCI_OPEN_ELEMENT) {
289         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
290             WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
291             return MCIERR_NO_ELEMENT_ALLOWED;
292         }
293         if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
294             lpOpenParms->lpstrElementName[2])
295         {
296             WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms->lpstrElementName);
297             ret = MCIERR_NO_ELEMENT_ALLOWED; 
298             goto the_error;
299         }
300         drive = toupper(lpOpenParms->lpstrElementName[0]);
301         strcpy(root, "A:\\");
302         root[0] = drive;
303         if (GetDriveTypeA(root) != DRIVE_CDROM)
304         {
305             ret = MCIERR_INVALID_DEVICE_NAME;
306             goto the_error;
307         }
308     }
309     else
310     {
311         /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
312         strcpy(root, "A:\\");
313         for (count = 0; root[0] <= 'Z'; root[0]++)
314         {
315             if (GetDriveTypeA(root) == DRIVE_CDROM && ++count >= dwDeviceID)
316             {
317                 drive = root[0];
318                 break;
319             }
320         }
321         if (!drive)
322         {
323             ret = MCIERR_INVALID_DEVICE_ID;
324             goto the_error;
325         }
326     }
327
328     wmcda->wNotifyDeviceID = dwDeviceID;
329     wmcda->dwTimeFormat = MCI_FORMAT_MSF;
330
331     /* now, open the handle */
332     strcpy(root, "\\\\.\\A:");
333     root[4] = drive;
334     wmcda->handle = CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
335     if (wmcda->handle != INVALID_HANDLE_VALUE)
336         return 0;
337
338  the_error:
339     --wmcda->nUseCount;
340     return ret;
341 }
342
343 /**************************************************************************
344  *                              MCICDA_Close                    [internal]
345  */
346 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
347 {
348     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
349
350     TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
351
352     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
353     
354     if (--wmcda->nUseCount == 0) {
355         CloseHandle(wmcda->handle);
356     }
357     return 0;
358 }
359
360 /**************************************************************************
361  *                              MCICDA_GetDevCaps               [internal]
362  */
363 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags, 
364                                    LPMCI_GETDEVCAPS_PARMS lpParms)
365 {
366     DWORD       ret = 0;
367
368     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
369
370     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
371
372     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
373         TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
374
375         switch (lpParms->dwItem) {
376         case MCI_GETDEVCAPS_CAN_RECORD:
377             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
378             ret = MCI_RESOURCE_RETURNED;
379             break;
380         case MCI_GETDEVCAPS_HAS_AUDIO:
381             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
382             ret = MCI_RESOURCE_RETURNED;
383             break;
384         case MCI_GETDEVCAPS_HAS_VIDEO:
385             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
386             ret = MCI_RESOURCE_RETURNED;
387             break;
388         case MCI_GETDEVCAPS_DEVICE_TYPE:
389             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
390             ret = MCI_RESOURCE_RETURNED;
391             break;
392         case MCI_GETDEVCAPS_USES_FILES:
393             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
394             ret = MCI_RESOURCE_RETURNED;
395             break;
396         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
397             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
398             ret = MCI_RESOURCE_RETURNED;
399             break;
400         case MCI_GETDEVCAPS_CAN_EJECT:
401             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
402             ret = MCI_RESOURCE_RETURNED;
403             break;
404         case MCI_GETDEVCAPS_CAN_PLAY:
405             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
406             ret = MCI_RESOURCE_RETURNED;
407             break;
408         case MCI_GETDEVCAPS_CAN_SAVE:
409             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
410             ret = MCI_RESOURCE_RETURNED;
411             break;
412         default:
413             ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
414             return MCIERR_UNRECOGNIZED_COMMAND;
415         }
416     } else {
417         TRACE("No GetDevCaps-Item !\n");
418         return MCIERR_UNRECOGNIZED_COMMAND;
419     }
420     TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
421     return ret;
422 }
423
424 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
425 {
426     unsigned long serial = 0;
427     int i;
428     WORD wMagic;
429     DWORD dwStart, dwEnd;
430  
431     /*
432      * wMagic collects the wFrames from track 1
433      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
434      * frames.
435      * There it is collected for correcting the serial when there are less than
436      * 3 tracks.
437      */
438     wMagic = toc->TrackData[0].Address[3];
439     dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
440
441     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
442         serial += (toc->TrackData[i].Address[1] << 16) | 
443             (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
444     }
445     dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
446  
447     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
448         serial += wMagic + (dwEnd - dwStart);
449
450     return serial;
451 }
452     
453
454 /**************************************************************************
455  *                              MCICDA_Info                     [internal]
456  */
457 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
458 {
459     LPSTR               str = NULL;
460     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
461     DWORD               ret = 0;
462     char                buffer[16];
463
464     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
465     
466     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
467         return MCIERR_NULL_PARAMETER_BLOCK;
468     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
469
470     TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
471     
472     if (dwFlags & MCI_INFO_PRODUCT) {
473         str = "Wine's audio CD";
474     } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
475         ret = MCIERR_NO_IDENTITY;
476     } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
477         DWORD       res = 0;
478         CDROM_TOC   toc;
479         DWORD       br;
480
481         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
482                              &toc, sizeof(toc), &br, NULL)) {
483             return MCICDA_GetError(wmcda);
484         }
485
486         res = CDROM_Audio_GetSerial(&toc);
487         sprintf(buffer, "%lu", res);
488         str = buffer;
489     } else {
490         WARN("Don't know this info command (%lu)\n", dwFlags);
491         ret = MCIERR_UNRECOGNIZED_COMMAND;
492     }
493     if (str) {
494         if (lpParms->dwRetSize <= strlen(str)) {
495             lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
496             ret = MCIERR_PARAM_OVERFLOW;
497         } else {
498             strcpy(lpParms->lpstrReturn, str);
499         }       
500     } else {
501         *lpParms->lpstrReturn = 0;
502     }
503     TRACE("=> %s (%ld)\n", lpParms->lpstrReturn, ret);
504     return ret;
505 }
506
507 /**************************************************************************
508  *                              MCICDA_Status                   [internal]
509  */
510 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
511 {
512     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
513     DWORD                       idx;
514     DWORD                       ret = 0;
515     CDROM_SUB_Q_DATA_FORMAT     fmt;
516     SUB_Q_CHANNEL_DATA          data;
517     CDROM_TOC                   toc;
518     DWORD                       br;
519
520     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
521     
522     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
523     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
524
525     if (dwFlags & MCI_NOTIFY) {
526         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
527         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
528                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
529     }
530     if (dwFlags & MCI_STATUS_ITEM) {
531         TRACE("dwItem = %lx\n", lpParms->dwItem);
532         switch (lpParms->dwItem) {
533         case MCI_STATUS_CURRENT_TRACK:
534             fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
535             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 
536                                  &data, sizeof(data), &br, NULL))
537             {
538                 return MCICDA_GetError(wmcda);
539             }
540             lpParms->dwReturn = data.CurrentPosition.TrackNumber;
541             TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
542             break;
543         case MCI_STATUS_LENGTH:
544             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
545                                  &toc, sizeof(toc), &br, NULL)) {
546                 WARN("error reading TOC !\n");
547                 return MCICDA_GetError(wmcda);
548             }
549             if (dwFlags & MCI_TRACK) {
550                 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
551                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
552                     return MCIERR_OUTOFRANGE;
553                 idx = lpParms->dwTrack - toc.FirstTrack;
554                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) - 
555                     FRAME_OF_TOC(toc, lpParms->dwTrack);
556                 /* Windows returns one frame less than the total track length for the
557                    last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
558                 if (lpParms->dwTrack == toc.LastTrack)
559                     lpParms->dwReturn--;
560             } else {
561                 /* Sum of the lengths of all of the tracks.  Inherits the
562                    'off by one frame' behavior from the length of the last track.
563                    See above comment. */
564                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
565                     FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
566             }
567             lpParms->dwReturn = MCICDA_CalcTime(wmcda, 
568                                                  (wmcda->dwTimeFormat == MCI_FORMAT_TMSF) 
569                                                     ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
570                                                  lpParms->dwReturn,
571                                                  &ret);
572             TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
573             break;
574         case MCI_STATUS_MODE:
575             lpParms->dwReturn = MCICDA_GetStatus(wmcda);
576             TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
577             lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
578             ret = MCI_RESOURCE_RETURNED;
579             break;
580         case MCI_STATUS_MEDIA_PRESENT:
581             lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
582                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
583             TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
584             ret = MCI_RESOURCE_RETURNED;
585             break;
586         case MCI_STATUS_NUMBER_OF_TRACKS:
587             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
588                                  &toc, sizeof(toc), &br, NULL)) {
589                 WARN("error reading TOC !\n");
590                 return MCICDA_GetError(wmcda);
591             }
592             lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
593             TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
594             if (lpParms->dwReturn == (WORD)-1) 
595                 return MCICDA_GetError(wmcda);
596             break;
597         case MCI_STATUS_POSITION:
598             if (dwFlags & MCI_STATUS_START) {
599                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
600                                      &toc, sizeof(toc), &br, NULL)) {
601                     WARN("error reading TOC !\n");
602                     return MCICDA_GetError(wmcda);
603                 }
604                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
605                 TRACE("get MCI_STATUS_START !\n");
606             } else if (dwFlags & MCI_TRACK) {
607                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
608                                      &toc, sizeof(toc), &br, NULL)) {
609                     WARN("error reading TOC !\n");
610                     return MCICDA_GetError(wmcda);
611                 }
612                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
613                     return MCIERR_OUTOFRANGE;
614                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
615                 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
616             } else {
617                 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
618                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 
619                                      &data, sizeof(data), &br, NULL)) {
620                     return MCICDA_GetError(wmcda);
621                 }
622                 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
623             }
624             lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
625             TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
626             break;
627         case MCI_STATUS_READY:
628             TRACE("MCI_STATUS_READY !\n");
629             switch (MCICDA_GetStatus(wmcda))
630             {
631             case MCI_MODE_NOT_READY:
632             case MCI_MODE_OPEN:
633                 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
634                 break;
635             default:
636                 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
637                 break;
638             }
639             TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
640             ret = MCI_RESOURCE_RETURNED;
641             break;
642         case MCI_STATUS_TIME_FORMAT:
643             lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, wmcda->dwTimeFormat);
644             TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
645             ret = MCI_RESOURCE_RETURNED;
646             break;
647         case 4001: /* FIXME: for bogus FullCD */
648         case MCI_CDA_STATUS_TYPE_TRACK:
649             if (!(dwFlags & MCI_TRACK)) 
650                 ret = MCIERR_MISSING_PARAMETER;
651             else {
652                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
653                                      &toc, sizeof(toc), &br, NULL)) {
654                     WARN("error reading TOC !\n");
655                     return MCICDA_GetError(wmcda);
656                 }
657                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
658                     ret = MCIERR_OUTOFRANGE;
659                 else
660                     lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
661                                          MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
662             }
663             TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
664             break;
665         default:
666             FIXME("unknown command %08lX !\n", lpParms->dwItem);
667             return MCIERR_UNRECOGNIZED_COMMAND;
668         }
669     } else {
670         WARN("not MCI_STATUS_ITEM !\n");
671     }
672     return ret;
673 }
674
675 /**************************************************************************
676  *                              MCICDA_Play                     [internal]
677  */
678 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
679 {
680     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
681     DWORD                       ret = 0, start, end;
682     CDROM_TOC                   toc;
683     DWORD                       br;
684     CDROM_PLAY_AUDIO_MSF        play;
685     CDROM_SUB_Q_DATA_FORMAT     fmt;
686     SUB_Q_CHANNEL_DATA          data;
687  
688     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
689     
690     if (lpParms == NULL)
691         return MCIERR_NULL_PARAMETER_BLOCK;
692
693     if (wmcda == NULL)
694         return MCIERR_INVALID_DEVICE_ID;
695
696     if (dwFlags & MCI_FROM) {
697         start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
698         TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
699     } else {
700         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
701         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 
702                              &data, sizeof(data), &br, NULL)) {
703             return MCICDA_GetError(wmcda);
704         }
705         start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
706     }
707     if (dwFlags & MCI_TO) {
708         end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
709         TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
710     } else {
711         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
712                              &toc, sizeof(toc), &br, NULL)) {
713             WARN("error reading TOC !\n");
714             return MCICDA_GetError(wmcda);
715         }
716         end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
717     }
718     TRACE("Playing from %lu to %lu\n", start, end);
719     play.StartingM = start / CDFRAMES_PERMIN;
720     play.StartingS = (start / CDFRAMES_PERSEC) % 60;
721     play.StartingF = start % CDFRAMES_PERSEC;
722     play.EndingM   = end / CDFRAMES_PERMIN;
723     play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
724     play.EndingF   = end % CDFRAMES_PERSEC;
725     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play), 
726                          NULL, 0, &br, NULL)) {
727         ret = MCIERR_HARDWARE;
728     } else if (dwFlags & MCI_NOTIFY) {
729         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
730         /*
731           mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
732           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
733         */
734     }
735     return ret;
736 }
737
738 /**************************************************************************
739  *                              MCICDA_Stop                     [internal]
740  */
741 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
742 {
743     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
744     DWORD               br;
745
746     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
747     
748     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
749     
750     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
751         return MCIERR_HARDWARE;
752
753     if (lpParms && (dwFlags & MCI_NOTIFY)) {
754         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
755         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
756                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
757     }
758     return 0;
759 }
760
761 /**************************************************************************
762  *                              MCICDA_Pause                    [internal]
763  */
764 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
765 {
766     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
767     DWORD               br;
768
769     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
770     
771     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
772     
773     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
774         return MCIERR_HARDWARE;
775
776     if (lpParms && (dwFlags & MCI_NOTIFY)) {
777         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
778         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
779                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
780     }
781     return 0;
782 }
783
784 /**************************************************************************
785  *                              MCICDA_Resume                   [internal]
786  */
787 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
788 {
789     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
790     DWORD               br;
791     
792     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
793     
794     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
795     
796     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
797         return MCIERR_HARDWARE;
798
799     if (lpParms && (dwFlags & MCI_NOTIFY)) {
800         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
801         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
802                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
803     }
804     return 0;
805 }
806
807 /**************************************************************************
808  *                              MCICDA_Seek                     [internal]
809  */
810 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
811 {
812     DWORD                       at;
813     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
814     CDROM_SEEK_AUDIO_MSF        seek;
815     CDROM_TOC                   toc;
816     DWORD                       br;
817
818     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
819     
820     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
821     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
822     
823     switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
824     case MCI_SEEK_TO_START:
825         TRACE("Seeking to start\n");
826         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
827                              &toc, sizeof(toc), &br, NULL)) {
828             WARN("error reading TOC !\n");
829             return MCICDA_GetError(wmcda);
830         }
831         at = FRAME_OF_TOC(toc, toc.FirstTrack);
832         break;
833     case MCI_SEEK_TO_END:
834         TRACE("Seeking to end\n");
835         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 
836                              &toc, sizeof(toc), &br, NULL)) {
837             WARN("error reading TOC !\n");
838             return MCICDA_GetError(wmcda);
839         }
840         at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
841         break;
842     case MCI_TO:
843         TRACE("Seeking to %lu\n", lpParms->dwTo);
844         at = lpParms->dwTo;
845         break;
846     default:
847         TRACE("Unknown seek action %08lX\n",
848               (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
849         return MCIERR_UNSUPPORTED_FUNCTION;
850     }
851     seek.M = at / CDFRAMES_PERMIN;
852     seek.S = (at / CDFRAMES_PERSEC) % 60;
853     seek.F = at % CDFRAMES_PERSEC;
854     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek), 
855                          NULL, 0, &br, NULL))
856         return MCIERR_HARDWARE;
857
858     if (dwFlags & MCI_NOTIFY) {
859         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
860         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
861                           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
862     }
863     return 0;
864 }
865
866 /**************************************************************************
867  *                              MCICDA_SetDoor                  [internal]
868  */
869 static DWORD    MCICDA_SetDoor(UINT wDevID, BOOL open)
870 {
871     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
872     DWORD               br;
873
874     TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
875     
876     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
877     
878     if (!DeviceIoControl(wmcda->handle, 
879                          (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
880                          NULL, 0, NULL, 0, &br, NULL))
881         return MCIERR_HARDWARE;
882       
883     return 0;
884 }
885
886 /**************************************************************************
887  *                              MCICDA_Set                      [internal]
888  */
889 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
890 {
891     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
892     
893     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
894     
895     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
896
897     if (dwFlags & MCI_SET_DOOR_OPEN) {
898         MCICDA_SetDoor(wDevID, TRUE);
899     }
900     if (dwFlags & MCI_SET_DOOR_CLOSED) {
901         MCICDA_SetDoor(wDevID, FALSE);
902     }
903
904     /* only functions which require valid lpParms below this line ! */
905     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
906     /*
907       TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
908       TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
909     */
910     if (dwFlags & MCI_SET_TIME_FORMAT) {
911         switch (lpParms->dwTimeFormat) {
912         case MCI_FORMAT_MILLISECONDS:
913             TRACE("MCI_FORMAT_MILLISECONDS !\n");
914             break;
915         case MCI_FORMAT_MSF:
916             TRACE("MCI_FORMAT_MSF !\n");
917             break;
918         case MCI_FORMAT_TMSF:
919             TRACE("MCI_FORMAT_TMSF !\n");
920             break;
921         default:
922             WARN("bad time format !\n");
923             return MCIERR_BAD_TIME_FORMAT;
924         }
925         wmcda->dwTimeFormat = lpParms->dwTimeFormat;
926     }
927     if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
928     if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
929     if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
930     if (dwFlags & MCI_NOTIFY) {
931         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", 
932               lpParms->dwCallback);
933         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
934                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
935     }
936     return 0;
937 }
938
939 /**************************************************************************
940  *                      DriverProc (MCICDA.@)
941  */
942 LONG CALLBACK   MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
943                                       DWORD dwParam1, DWORD dwParam2)
944 {
945     switch(wMsg) {
946     case DRV_LOAD:              return 1;
947     case DRV_FREE:              return 1;
948     case DRV_OPEN:              return MCICDA_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
949     case DRV_CLOSE:             return MCICDA_drvClose(dwDevID);
950     case DRV_ENABLE:            return 1;       
951     case DRV_DISABLE:           return 1;
952     case DRV_QUERYCONFIGURE:    return 1;
953     case DRV_CONFIGURE:         MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
954     case DRV_INSTALL:           return DRVCNF_RESTART;
955     case DRV_REMOVE:            return DRVCNF_RESTART;
956         
957     case MCI_OPEN_DRIVER:       return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
958     case MCI_CLOSE_DRIVER:      return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
959     case MCI_GETDEVCAPS:        return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
960     case MCI_INFO:              return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSA)dwParam2);
961     case MCI_STATUS:            return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
962     case MCI_SET:               return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
963     case MCI_PLAY:              return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
964     case MCI_STOP:              return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
965     case MCI_PAUSE:             return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
966     case MCI_RESUME:            return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
967     case MCI_SEEK:              return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
968     /* FIXME: I wonder if those two next items are really called ? */
969     case MCI_SET_DOOR_OPEN:     FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
970                                 return MCICDA_SetDoor(dwDevID, TRUE);
971     case MCI_SET_DOOR_CLOSED:   FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
972                                 return MCICDA_SetDoor(dwDevID, FALSE);
973     /* commands that should be supported */
974     case MCI_LOAD:              
975     case MCI_SAVE:              
976     case MCI_FREEZE:            
977     case MCI_PUT:               
978     case MCI_REALIZE:           
979     case MCI_UNFREEZE:          
980     case MCI_UPDATE:            
981     case MCI_WHERE:             
982     case MCI_STEP:              
983     case MCI_SPIN:              
984     case MCI_ESCAPE:            
985     case MCI_COPY:              
986     case MCI_CUT:               
987     case MCI_DELETE:            
988     case MCI_PASTE:             
989         FIXME("Unsupported yet command [%lu]\n", wMsg);
990         break;
991     /* commands that should report an error */
992     case MCI_WINDOW:            
993         TRACE("Unsupported command [%lu]\n", wMsg);
994         break;
995     case MCI_OPEN:
996     case MCI_CLOSE:
997         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
998         break;
999     default:
1000         TRACE("Sending msg [%lu] to default driver proc\n", wMsg);
1001         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1002     }
1003     return MCIERR_UNRECOGNIZED_COMMAND;
1004 }
1005
1006 /*-----------------------------------------------------------------------*/