Better implementation of GetCalendarInfo{A,W}, not perfect.
[wine] / dlls / ntdll / cdrom.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /* Main file for CD-ROM support
3  *
4  * Copyright 1994 Martin Ayotte
5  * Copyright 1999, 2001 Eric Pouech
6  * Copyright 2000 Andreas Mohr
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include "ntddk.h"
35 #include "winioctl.h"
36 #include "ntddstor.h"
37 #include "ntddcdrm.h"
38 #include "ntddscsi.h"
39 #include "drive.h"
40 #include "file.h"
41 #include "wine/debug.h"
42
43 #ifdef HAVE_SCSI_SG_H
44 # include <scsi/sg.h>
45 #endif
46 #ifdef HAVE_LINUX_MAJOR_H
47 # include <linux/major.h>
48 #endif
49 #ifdef HAVE_LINUX_HDREG_H
50 # include <linux/hdreg.h>
51 #endif
52 #ifdef HAVE_LINUX_PARAM_H
53 # include <linux/param.h>
54 #endif
55 #ifdef HAVE_LINUX_CDROM_H
56 # include <linux/cdrom.h>
57 #endif
58 #ifdef HAVE_LINUX_UCDROM_H
59 # include <linux/ucdrom.h>
60 #endif
61 #ifdef HAVE_SYS_CDIO_H
62 # include <sys/cdio.h>
63 #endif
64
65 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
66
67 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom 
68  * this should be removed when a proper device interface is implemented
69  */
70 struct cdrom_cache {
71     int fd;
72     int count;
73 };
74 static struct cdrom_cache cdrom_cache[26];
75
76 /******************************************************************
77  *              CDROM_GetIdeInterface
78  *
79  * Determines the ide interface (the number after the ide), and the 
80  * number of the device on that interface for ide cdroms.
81  * Returns false if the info could not be get
82  *
83  * NOTE: this function is used in CDROM_InitRegistry and CDROM_GetAddress
84  */
85 static int CDROM_GetIdeInterface(int dev, int* iface, int* device)
86 {
87 #if defined(linux)
88     {
89         struct stat st;
90         if (ioctl(dev, SG_EMULATED_HOST) != -1) {
91             FIXME("not implemented for true scsi drives\n");
92             return 0;
93         }
94         if ( fstat(dev, &st) == -1 || ! S_ISBLK(st.st_mode)) {
95             FIXME("cdrom not a block device!!!\n");
96             return 0;
97         }
98         switch (major(st.st_rdev)) {
99             case IDE0_MAJOR: *iface = 0; break;
100             case IDE1_MAJOR: *iface = 1; break;
101             case IDE2_MAJOR: *iface = 2; break;
102             case IDE3_MAJOR: *iface = 3; break;
103             case IDE4_MAJOR: *iface = 4; break;
104             case IDE5_MAJOR: *iface = 5; break;
105             case IDE6_MAJOR: *iface = 6; break;
106             case IDE7_MAJOR: *iface = 7; break;
107             default:
108                              FIXME("major %d not supported\n", major(st.st_rdev));
109         }
110         *device = (minor(st.st_rdev) == 63 ? 1 : 0);
111         return 1;
112     }
113 #elif defined(__FreeBSD__) || defined(__NetBSD__)
114     FIXME("not implemented for BSD\n");
115     return 0;
116 #else
117     FIXME("not implemented for nonlinux\n");
118     return 0;
119 #endif
120 }
121
122
123 /******************************************************************
124  *              CDROM_InitRegistry
125  *
126  * Initializes registry to contain scsi info about the cdrom in NT.
127  * All devices (even not real scsi ones) have this info in NT.
128  * TODO: for now it only works for non scsi devices
129  * NOTE: programs usually read these registry entries after sending the 
130  *       IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
131  */
132 void CDROM_InitRegistry(int dev)
133 {
134     int portnum, targetid;
135     int dma;
136     OBJECT_ATTRIBUTES attr;
137     UNICODE_STRING nameW;
138     WCHAR dataW[50];
139     DWORD lenW;
140     char buffer[40];
141     DWORD value;
142     const char *data;
143     HKEY scsiKey;
144     HKEY portKey;
145     HKEY busKey;
146     HKEY targetKey;
147     DWORD disp;
148
149     attr.Length = sizeof(attr);
150     attr.RootDirectory = HKEY_LOCAL_MACHINE;
151     attr.ObjectName = &nameW;
152     attr.Attributes = 0;
153     attr.SecurityDescriptor = NULL;
154     attr.SecurityQualityOfService = NULL;
155
156     if ( ! CDROM_GetIdeInterface(dev, &portnum, &targetid))
157         return;
158
159     /* Ensure there is Scsi key */
160     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "HARDWARE\\DEVICEMAP\\Scsi" ) ||
161         NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
162                      NULL, REG_OPTION_VOLATILE, &disp ))
163     {
164         ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
165         return;
166     }
167     RtlFreeUnicodeString( &nameW );
168
169     snprintf(buffer,40,"Scsi Port %d",portnum);
170     attr.RootDirectory = scsiKey;
171     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
172         NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
173                      NULL, REG_OPTION_VOLATILE, &disp ))
174     {
175         ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
176         return;
177     }
178     RtlFreeUnicodeString( &nameW );
179
180     RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
181     data = "atapi";
182     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
183     NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
184     RtlFreeUnicodeString( &nameW );
185     value = 10;
186     RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
187     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
188     RtlFreeUnicodeString( &nameW );
189     value = 0;
190 #ifdef HDIO_GET_DMA
191     if (ioctl(dev,HDIO_GET_DMA, &dma) != -1) {
192         value = dma;
193         TRACE("setting dma to %lx\n", value);
194     }
195 #endif
196     RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
197     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
198     RtlFreeUnicodeString( &nameW );
199
200     attr.RootDirectory = portKey;
201     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Scsi Bus 0" ) ||
202         NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
203                      NULL, REG_OPTION_VOLATILE, &disp ))
204     {
205         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
206         return;
207     }
208     RtlFreeUnicodeString( &nameW );
209
210     attr.RootDirectory = busKey;
211     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
212         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
213                      NULL, REG_OPTION_VOLATILE, &disp ))
214     {
215         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
216         return;
217     }
218     RtlFreeUnicodeString( &nameW );
219     NtClose( targetKey );
220
221     snprintf(buffer,40,"Target Id %d", targetid);
222     attr.RootDirectory = busKey;
223     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
224         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
225                      NULL, REG_OPTION_VOLATILE, &disp ))
226     {
227         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
228         return;
229     }
230     RtlFreeUnicodeString( &nameW );
231
232     RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
233     data = "CdRomPeripheral";
234     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
235     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
236     RtlFreeUnicodeString( &nameW );
237     /* FIXME - maybe read the real identifier?? */
238     RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
239     data = "Wine CDROM";
240     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
241     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
242     RtlFreeUnicodeString( &nameW );
243     /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */
244     RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
245     data = "Cdrom0";
246     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
247     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
248     RtlFreeUnicodeString( &nameW );
249
250     NtClose( targetKey );
251     NtClose( busKey );
252     NtClose( portKey );
253     NtClose( scsiKey );
254 }
255
256
257 /******************************************************************
258  *              CDROM_Open
259  *
260  *
261  */
262 static int CDROM_Open(HANDLE hDevice, DWORD clientID)
263 {
264     int dev = LOWORD(clientID);
265
266     if (dev >= 26) return -1;
267
268     if (!cdrom_cache[dev].count)
269     {
270         char root[4];
271
272         strcpy(root, "A:\\");
273         root[0] += dev;
274         if (GetDriveTypeA(root) != DRIVE_CDROM) return -1;
275         cdrom_cache[dev].fd = open(DRIVE_GetDevice(dev), O_RDONLY|O_NONBLOCK);
276         if (cdrom_cache[dev].fd == -1)
277         {
278             FIXME("Can't open %s: %s\n", root, strerror(errno));
279             return -1;
280         }
281     }
282     cdrom_cache[dev].count++;
283     return cdrom_cache[dev].fd;
284 }
285
286 /******************************************************************
287  *              CDROM_Close
288  *
289  *
290  */
291 static void CDROM_Close(DWORD clientID, int fd)
292 {
293     int dev = LOWORD(clientID);
294     
295     if (dev >= 26 || fd != cdrom_cache[dev].fd) FIXME("how come\n");
296     if (--cdrom_cache[dev].count == 0)
297         close(cdrom_cache[dev].fd);
298 }
299
300 /******************************************************************
301  *              CDROM_GetStatusCode
302  *
303  *
304  */
305 static DWORD CDROM_GetStatusCode(int io)
306 {
307     if (io == 0) return 0;
308     switch (errno)
309     {
310     case EIO:
311 #ifdef ENOMEDIUM
312     case ENOMEDIUM:
313 #endif
314             return STATUS_NO_MEDIA_IN_DEVICE;
315     }
316     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
317     return STATUS_IO_DEVICE_ERROR;
318 }
319
320 static DWORD CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
321 {
322     cac->LbaFormat = 0; /* FIXME */
323     cac->LogicalBlocksPerSecond = 1; /* FIXME */
324     return  STATUS_NOT_SUPPORTED;
325 }
326
327 static DWORD CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
328 {
329     return STATUS_NOT_SUPPORTED;
330 }
331
332 static DWORD CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
333 {
334 #if 0
335     dg->Cylinders.s.LowPart = 1; /* FIXME */
336     dg->Cylinders.s.HighPart = 0; /* FIXME */
337     dg->MediaType = 1; /* FIXME */
338     dg->TracksPerCylinder = 1; /* FIXME */
339     dg->SectorsPerTrack = 1; /* FIXME */
340     dg->BytesPerSector= 1; /* FIXME */
341 #endif
342     return STATUS_NOT_SUPPORTED;
343 }
344
345 /**************************************************************************
346  *                              CDROM_Reset                     [internal]
347  */
348 static DWORD CDROM_ResetAudio(int dev)
349 {
350 #if defined(linux)
351     return CDROM_GetStatusCode(ioctl(dev, CDROMRESET));
352 #elif defined(__FreeBSD__) || defined(__NetBSD__)
353     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESET, NULL));
354 #else
355     return STATUS_NOT_SUPPORTED;
356 #endif
357 }  
358
359 /******************************************************************
360  *              CDROM_SetTray
361  *
362  *
363  */
364 static DWORD CDROM_SetTray(int dev, BOOL doEject)
365 {
366 #if defined(linux)
367     return CDROM_GetStatusCode(ioctl(dev, doEject ? CDROMEJECT : CDROMCLOSETRAY));
368 #elif defined(__FreeBSD__) || defined(__NetBSD__)
369     return CDROM_GetStatusCode((ioctl(dev, CDIOCALLOW, NULL)) || 
370                                (ioctl(dev, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
371                                (ioctl(dev, CDIOCPREVENT, NULL)));
372 #else
373     return STATUS_NOT_SUPPORTED;
374 #endif
375 }
376
377 /******************************************************************
378  *              CDROM_ControlEjection
379  *
380  *
381  */
382 static DWORD CDROM_ControlEjection(int dev, const PREVENT_MEDIA_REMOVAL* rmv)
383 {
384 #if defined(linux)
385     return CDROM_GetStatusCode(ioctl(dev, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
386 #elif defined(__FreeBSD__) || defined(__NetBSD__)
387     return CDROM_GetStatusCode(ioctl(dev, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
388 #else
389     return STATUS_NOT_SUPPORTED;
390 #endif
391 }
392
393 /******************************************************************
394  *              CDROM_ReadTOC
395  *
396  *
397  */
398 static DWORD CDROM_ReadTOC(int dev, CDROM_TOC* toc)
399 {
400     DWORD       ret = STATUS_NOT_SUPPORTED;
401
402 #if defined(linux)
403     int                         i, io = -1;
404     struct cdrom_tochdr         hdr;
405     struct cdrom_tocentry       entry;
406
407     io = ioctl(dev, CDROMREADTOCHDR, &hdr);
408     if (io == -1) 
409     {
410         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
411         goto end;
412     }
413     toc->FirstTrack = hdr.cdth_trk0;
414     toc->LastTrack  = hdr.cdth_trk1;
415
416     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
417
418     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
419     {
420         if (i == toc->LastTrack + 1)
421         {
422             entry.cdte_track = CDROM_LEADOUT;
423         } else {
424             entry.cdte_track = i;
425         }
426         entry.cdte_format = CDROM_MSF;
427         io = ioctl(dev, CDROMREADTOCENTRY, &entry);
428         if (io == -1) {
429             WARN("error read entry (%s)\n", strerror(errno));
430             goto end;
431         }
432         toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
433         toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
434         /* marking last track with leadout value as index */
435         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
436         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
437         toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
438         toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
439         toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
440     }
441 end:
442     ret = CDROM_GetStatusCode(io);
443 #elif defined(__FreeBSD__) || defined(__NetBSD__)
444     int                         i, io = -1;
445     struct ioc_toc_header       hdr;
446     struct ioc_read_toc_entry   entry;
447     struct cd_toc_entry         toc_buffer;
448
449     io = ioctl(dev, CDIOREADTOCHEADER, &hdr);
450     if (io == -1) 
451     {
452         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
453         goto end;
454     }
455     toc->FirstTrack = hdr.starting_track;
456     toc->LastTrack  = hdr.ending_track;
457
458     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
459
460     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
461     {
462         if (i == toc->LastTrack + 1)
463         {
464 #define LEADOUT 0xaa
465             entry.starting_track = LEADOUT;
466         } else {
467             entry.starting_track = i;
468         }
469         memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
470         entry.address_format = CD_MSF_FORMAT;
471         entry.data_len = sizeof(toc_buffer);
472         entry.data = &toc_buffer;
473         io = ioctl(dev, CDIOREADTOCENTRYS, &entry);
474         if (io == -1) {
475             WARN("error read entry (%s)\n", strerror(errno));
476             goto end;
477         }
478         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
479         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
480         /* marking last track with leadout value as index */
481         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
482         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
483         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
484         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
485         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
486     }
487 end:
488     ret = CDROM_GetStatusCode(io);
489 #else
490     ret = STATUS_NOT_SUPPORTED;
491 #endif
492     return ret;
493 }
494
495 /******************************************************************
496  *              CDROM_GetDiskData
497  *
498  *
499  */
500 static DWORD CDROM_GetDiskData(int dev, CDROM_DISK_DATA* data)
501 {
502     CDROM_TOC   toc;
503     DWORD       ret;
504     int         i;
505
506     if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
507     data->DiskData = 0;
508     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
509         if (toc.TrackData[i].Control & 0x04)
510             data->DiskData |= CDROM_DISK_DATA_TRACK;
511         else
512             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
513     }
514     return 0;
515 }
516
517 /******************************************************************
518  *              CDROM_ReadQChannel
519  *
520  *
521  */
522 static DWORD CDROM_ReadQChannel(int dev, const CDROM_SUB_Q_DATA_FORMAT* fmt, 
523                                 SUB_Q_CHANNEL_DATA* data)
524 {
525     DWORD               ret = STATUS_NOT_SUPPORTED;
526 #ifdef linux
527     unsigned            size;
528     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
529     int                 io;
530     struct cdrom_subchnl        sc;
531     sc.cdsc_format = CDROM_MSF;
532
533     io = ioctl(dev, CDROMSUBCHNL, &sc);
534     if (io == -1)
535     {
536         TRACE("opened or no_media (%s)!\n", strerror(errno));
537         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
538         goto end;
539     }
540
541     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
542
543     switch (sc.cdsc_audiostatus) {
544     case CDROM_AUDIO_INVALID:
545         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
546         break;
547     case CDROM_AUDIO_NO_STATUS: 
548         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
549         break;
550     case CDROM_AUDIO_PLAY:
551         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
552         break;
553     case CDROM_AUDIO_PAUSED:
554         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
555         break;
556     case CDROM_AUDIO_COMPLETED:
557         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
558         break;
559     case CDROM_AUDIO_ERROR:
560         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
561         break;
562     default:
563         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
564         break;
565     }
566     switch (fmt->Format)
567     {
568     case IOCTL_CDROM_CURRENT_POSITION:
569         size = sizeof(SUB_Q_CURRENT_POSITION);
570         data->CurrentPosition.FormatCode = sc.cdsc_format;
571         data->CurrentPosition.Control = sc.cdsc_ctrl;
572         data->CurrentPosition.ADR = sc.cdsc_adr;
573         data->CurrentPosition.TrackNumber = sc.cdsc_trk;
574         data->CurrentPosition.IndexNumber = sc.cdsc_ind;
575
576         data->CurrentPosition.AbsoluteAddress[0] = 0;
577         data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
578         data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
579         data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
580
581         data->CurrentPosition.TrackRelativeAddress[0] = 0;
582         data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
583         data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
584         data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
585         break;
586     case IOCTL_CDROM_MEDIA_CATALOG:
587         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
588         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
589         {
590             struct cdrom_mcn mcn;
591             if ((io = ioctl(dev, CDROM_GET_MCN, &mcn)) == -1) goto end;
592
593             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
594             data->MediaCatalog.Mcval = 0; /* FIXME */
595             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
596             data->MediaCatalog.MediaCatalog[14] = 0;
597         }
598         break;
599     case IOCTL_CDROM_TRACK_ISRC:
600         size = sizeof(SUB_Q_CURRENT_POSITION);
601         FIXME("TrackIsrc: NIY on linux");
602         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
603         data->TrackIsrc.Tcval = 0;
604         io = 0;
605         break;
606     }
607
608  end:
609     ret = CDROM_GetStatusCode(io);
610 #elif defined(__FreeBSD__) || defined(__NetBSD__)
611     unsigned            size;
612     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
613     int                 io;
614     struct ioc_read_subchannel  read_sc;
615     struct cd_sub_channel_info  sc;
616
617     read_sc.address_format = CD_MSF_FORMAT;
618     read_sc.track          = 0;
619     read_sc.data_len       = sizeof(sc);
620     read_sc.data           = &sc;
621     switch (fmt->Format)
622     {
623     case IOCTL_CDROM_CURRENT_POSITION:
624         read_sc.data_format    = CD_CURRENT_POSITION;
625         break;
626     case IOCTL_CDROM_MEDIA_CATALOG:
627         read_sc.data_format    = CD_MEDIA_CATALOG;
628         break;
629     case IOCTL_CDROM_TRACK_ISRC:
630         read_sc.data_format    = CD_TRACK_INFO;
631         sc.what.track_info.track_number = data->TrackIsrc.Track;
632         break;
633     }
634     io = ioctl(dev, CDIOCREADSUBCHANNEL, &read_sc);
635     if (io == -1)
636     {
637         TRACE("opened or no_media (%s)!\n", strerror(errno));
638         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
639         goto end;
640     }
641
642     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
643
644     switch (sc.header.audio_status) {
645     case CD_AS_AUDIO_INVALID:
646         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
647         break;
648     case CD_AS_NO_STATUS:
649         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
650         break;
651     case CD_AS_PLAY_IN_PROGRESS:
652         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
653         break;
654     case CD_AS_PLAY_PAUSED:
655         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
656         break;
657     case CD_AS_PLAY_COMPLETED:
658         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
659         break;
660     case CD_AS_PLAY_ERROR:
661         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
662         break;
663     default:
664         TRACE("status=%02X !\n", sc.header.audio_status);
665     }
666     switch (fmt->Format)
667     {
668     case IOCTL_CDROM_CURRENT_POSITION:
669         size = sizeof(SUB_Q_CURRENT_POSITION);
670         data->CurrentPosition.FormatCode = sc.what.position.data_format;
671         data->CurrentPosition.Control = sc.what.position.control;
672         data->CurrentPosition.ADR = sc.what.position.addr_type;
673         data->CurrentPosition.TrackNumber = sc.what.position.track_number;
674         data->CurrentPosition.IndexNumber = sc.what.position.index_number;
675
676         data->CurrentPosition.AbsoluteAddress[0] = 0;
677         data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
678         data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
679         data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
680         data->CurrentPosition.TrackRelativeAddress[0] = 0;
681         data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
682         data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
683         data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
684         break;
685     case IOCTL_CDROM_MEDIA_CATALOG:
686         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
687         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
688         data->MediaCatalog.FormatCode = sc.what.media_catalog.data_format;
689         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
690         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
691         break;
692     case IOCTL_CDROM_TRACK_ISRC:
693         size = sizeof(SUB_Q_CURRENT_POSITION);
694         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
695         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
696         break;
697     }
698
699  end:
700     ret = CDROM_GetStatusCode(io);
701 #endif
702     return ret;
703 }
704
705 /******************************************************************
706  *              CDROM_Verify
707  *
708  *
709  */
710 static DWORD CDROM_Verify(int dev)
711 {
712     /* quick implementation */
713     CDROM_SUB_Q_DATA_FORMAT     fmt;
714     SUB_Q_CHANNEL_DATA          data;
715
716     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
717     return CDROM_ReadQChannel(dev, &fmt, &data) ? 0 : 1;
718 }
719
720 /******************************************************************
721  *              CDROM_PlayAudioMSF
722  *
723  *
724  */
725 static DWORD CDROM_PlayAudioMSF(int dev, const CDROM_PLAY_AUDIO_MSF* audio_msf)
726 {
727     DWORD       ret = STATUS_NOT_SUPPORTED;
728 #ifdef linux
729     struct      cdrom_msf       msf;
730     int         io;
731
732     msf.cdmsf_min0   = audio_msf->StartingM;
733     msf.cdmsf_sec0   = audio_msf->StartingS;
734     msf.cdmsf_frame0 = audio_msf->StartingF;
735     msf.cdmsf_min1   = audio_msf->EndingM;
736     msf.cdmsf_sec1   = audio_msf->EndingS;
737     msf.cdmsf_frame1 = audio_msf->EndingF;
738
739     io = ioctl(dev, CDROMSTART);
740     if (io == -1)
741     {
742         WARN("motor doesn't start !\n");
743         goto end;
744     }
745     io = ioctl(dev, CDROMPLAYMSF, &msf);
746     if (io == -1)
747     {
748         WARN("device doesn't play !\n");
749         goto end;
750     }
751     TRACE("msf = %d:%d:%d %d:%d:%d\n",
752           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
753           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
754  end:
755     ret = CDROM_GetStatusCode(io);
756 #elif defined(__FreeBSD__) || defined(__NetBSD__)
757     struct      ioc_play_msf    msf;
758     int         io;
759
760     msf.start_m      = audio_msf->StartingM;
761     msf.start_s      = audio_msf->StartingS;
762     msf.start_f      = audio_msf->StartingF;
763     msf.end_m        = audio_msf->EndingM;
764     msf.end_s        = audio_msf->EndingS;
765     msf.end_f        = audio_msf->EndingF;
766
767     io = ioctl(dev, CDIOCSTART, NULL);
768     if (io == -1)
769     {
770         WARN("motor doesn't start !\n");
771         goto end;
772     }
773     io = ioctl(dev, CDIOCPLAYMSF, &msf);
774     if (io == -1)
775     {
776         WARN("device doesn't play !\n");
777         goto end;
778     }
779     TRACE("msf = %d:%d:%d %d:%d:%d\n",
780           msf.start_m, msf.start_s, msf.start_f,
781           msf.end_m,   msf.end_s,   msf.end_f);
782 end:
783     ret = CDROM_GetStatusCode(io);
784 #endif
785     return ret;
786 }
787
788 /******************************************************************
789  *              CDROM_SeekAudioMSF
790  *
791  *
792  */
793 static DWORD CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
794 {
795 #if defined(linux)
796     struct cdrom_msf0   msf;
797     msf.minute = audio_msf->M;
798     msf.second = audio_msf->S;
799     msf.frame  = audio_msf->F;
800
801     return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
802 #elif defined(__FreeBSD__) || defined(__NetBSD__)
803     FIXME("Could a BSD expert implement the seek function ?\n");
804     return STATUS_NOT_SUPPORTED;
805 #else
806     return STATUS_NOT_SUPPORTED;
807 #endif
808 }
809
810 /******************************************************************
811  *              CDROM_PauseAudio
812  *
813  *
814  */
815 static DWORD CDROM_PauseAudio(int dev)
816 {
817 #if defined(linux)
818     return CDROM_GetStatusCode(ioctl(dev, CDROMPAUSE));
819 #elif defined(__FreeBSD__) || defined(__NetBSD__)
820     return CDROM_GetStatusCode(ioctl(dev, CDIOCPAUSE, NULL));
821 #else
822     return STATUS_NOT_SUPPORTED;
823 #endif
824 }
825
826 /******************************************************************
827  *              CDROM_ResumeAudio
828  *
829  *
830  */
831 static DWORD CDROM_ResumeAudio(int dev)
832 {
833 #if defined(linux)
834     return CDROM_GetStatusCode(ioctl(dev, CDROMRESUME));
835 #elif defined(__FreeBSD__) || defined(__NetBSD__)
836     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESUME, NULL));
837 #else
838     return STATUS_NOT_SUPPORTED;
839 #endif
840 }
841
842 /******************************************************************
843  *              CDROM_StopAudio
844  *
845  *
846  */
847 static DWORD CDROM_StopAudio(int dev)
848 {
849 #if defined(linux)
850     return CDROM_GetStatusCode(ioctl(dev, CDROMSTOP));
851 #elif defined(__FreeBSD__) || defined(__NetBSD__)
852     return CDROM_GetStatusCode(ioctl(dev, CDIOCSTOP, NULL));
853 #else
854     return STATUS_NOT_SUPPORTED;
855 #endif
856 }
857
858 /******************************************************************
859  *              CDROM_GetVolume
860  *
861  *
862  */
863 static DWORD CDROM_GetVolume(int dev, VOLUME_CONTROL* vc)
864 {
865 #if defined(linux)
866     struct cdrom_volctrl volc;
867     int io;
868     
869     io = ioctl(dev, CDROMVOLREAD, &volc);
870     if (io != -1)
871     {
872         vc->PortVolume[0] = volc.channel0;
873         vc->PortVolume[1] = volc.channel1;
874         vc->PortVolume[2] = volc.channel2;
875         vc->PortVolume[3] = volc.channel3;
876     }
877     return CDROM_GetStatusCode(io);
878 #elif defined(__FreeBSD__) || defined(__NetBSD__)
879     struct  ioc_vol     volc;
880     int io;
881     
882     io = ioctl(dev, CDIOCGETVOL, &volc);
883     if (io != -1)
884     {
885         vc->PortVolume[0] = volc.vol[0];
886         vc->PortVolume[1] = volc.vol[1];
887         vc->PortVolume[2] = volc.vol[2];
888         vc->PortVolume[3] = volc.vol[3];
889     } 
890     return CDROM_GetStatusCode(io);
891 #else
892     return STATUS_NOT_SUPPORTED;
893 #endif
894 }
895
896 /******************************************************************
897  *              CDROM_SetVolume
898  *
899  *
900  */
901 static DWORD CDROM_SetVolume(int dev, const VOLUME_CONTROL* vc)
902 {
903 #if defined(linux)
904     struct cdrom_volctrl volc;
905
906     volc.channel0 = vc->PortVolume[0];
907     volc.channel1 = vc->PortVolume[1];
908     volc.channel2 = vc->PortVolume[2];
909     volc.channel3 = vc->PortVolume[3];
910    
911     return CDROM_GetStatusCode(ioctl(dev, CDROMVOLCTRL, &volc));
912 #elif defined(__FreeBSD__) || defined(__NetBSD__)
913     struct  ioc_vol     volc;
914
915     volc.vol[0] = vc->PortVolume[0];
916     volc.vol[1] = vc->PortVolume[1];
917     volc.vol[2] = vc->PortVolume[2];
918     volc.vol[3] = vc->PortVolume[3];
919
920     return CDROM_GetStatusCode(ioctl(dev, CDIOCSETVOL, &volc));
921 #else
922     return STATUS_NOT_SUPPORTED;
923 #endif
924 }
925
926 /******************************************************************
927  *              CDROM_RawRead
928  *
929  *
930  */
931 static DWORD CDROM_RawRead(int dev, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
932 {
933     int         ret = STATUS_NOT_SUPPORTED;
934     int         io = -1;
935     DWORD       sectSize;
936
937     switch (raw->TrackMode)
938     {
939     case YellowMode2:   sectSize = 2336;        break;
940     case XAForm2:       sectSize = 2328;        break;
941     case CDDA:          sectSize = 2352;        break;
942     default:    return STATUS_INVALID_PARAMETER;
943     }
944     if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
945     /* strangely enough, it seems that sector offsets are always indicated with a size of 2048, 
946      * even if a larger size if read...
947      */
948 #if defined(linux)
949     {
950         struct cdrom_read       cdr;
951         struct cdrom_read_audio cdra;
952
953         switch (raw->TrackMode)
954         {
955         case YellowMode2:
956             if (raw->DiskOffset.s.HighPart) FIXME("Unsupported value\n");
957             cdr.cdread_lba = raw->DiskOffset.s.LowPart; /* FIXME ? */
958             cdr.cdread_bufaddr = buffer;
959             cdr.cdread_buflen = raw->SectorCount * sectSize;
960             io = ioctl(dev, CDROMREADMODE2, &cdr);
961             break;
962         case XAForm2:
963             FIXME("XAForm2: NIY\n");
964             return ret;
965         case CDDA:
966             /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
967              * between by NT2K box and this... should check on the same drive... 
968              * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
969              * (linux/NT).
970              * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
971              * talking of 0.2 ms of sound
972              */
973             /* 2048 = 2 ** 11 */
974             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
975             cdra.addr.lba = ((raw->DiskOffset.s.LowPart >> 11) |
976                 (raw->DiskOffset.s.HighPart << (32 - 11))) - 1;
977             FIXME("reading at %u\n", cdra.addr.lba);
978             cdra.addr_format = CDROM_LBA;
979             cdra.nframes = raw->SectorCount;
980             cdra.buf = buffer;
981             io = ioctl(dev, CDROMREADAUDIO, &cdra);
982             break;
983         }
984     }
985 #elif defined(__FreeBSD__)
986     {
987         struct ioc_read_audio   ira;
988
989         switch (raw->TrackMode)
990         {
991         case YellowMode2:
992             FIXME("YellowMode2: NIY\n");
993             return ret;
994         case XAForm2:
995             FIXME("XAForm2: NIY\n");
996             return ret;
997         case CDDA:
998             /* 2048 = 2 ** 11 */
999             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1000             ira.address.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1001                 raw->DiskOffset.s.HighPart << (32 - 11)) - 1;
1002             ira.address_format = CD_LBA_FORMAT;
1003             ira.nframes = raw->SectorCount;
1004             ira.buffer = buffer;
1005             io = ioctl(dev, CDIOCREADAUDIO, &ira);
1006             break;
1007         }
1008     }
1009 #elif defined(__NetBSD__)
1010     {
1011         switch (raw->TrackMode)
1012         {
1013         case YellowMode2:
1014             FIXME("YellowMode2: NIY\n");
1015             return ret;
1016         case XAForm2:
1017             FIXME("XAForm2: NIY\n");
1018             return ret;
1019         case CDDA:
1020             FIXME("CDDA: NIY\n");
1021             return ret;
1022         }
1023     }
1024 #endif
1025     *sz = sectSize * raw->SectorCount;
1026     ret = CDROM_GetStatusCode(io);
1027     return ret;
1028 }
1029
1030 /******************************************************************
1031  *              CDROM_ScsiPassThroughDirect
1032  *
1033  *
1034  */
1035 static DWORD CDROM_ScsiPassThroughDirect(int dev, PSCSI_PASS_THROUGH_DIRECT pPacket)
1036 {
1037     int ret = STATUS_NOT_SUPPORTED;
1038 #if defined(linux)
1039     struct cdrom_generic_command cmd;
1040     struct request_sense sense;
1041     int io;
1042
1043     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1044         return STATUS_BUFFER_TOO_SMALL;
1045
1046     if (pPacket->CdbLength > 12)
1047         return STATUS_INVALID_PARAMETER;
1048
1049     if (pPacket->SenseInfoLength > sizeof(sense))
1050         return STATUS_INVALID_PARAMETER;
1051
1052     memset(&cmd, 0, sizeof(cmd));
1053     memset(&sense, 0, sizeof(sense));
1054
1055     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1056
1057     cmd.buffer         = pPacket->DataBuffer;
1058     cmd.buflen         = pPacket->DataTransferLength;
1059     cmd.sense          = &sense;
1060     cmd.quiet          = 0;
1061     cmd.timeout        = pPacket->TimeOutValue*HZ;
1062
1063     switch (pPacket->DataIn)
1064     {
1065     case SCSI_IOCTL_DATA_OUT:
1066         cmd.data_direction = CGC_DATA_WRITE;
1067         break;
1068     case SCSI_IOCTL_DATA_IN:
1069         cmd.data_direction = CGC_DATA_READ;
1070         break;
1071     case SCSI_IOCTL_DATA_UNSPECIFIED:
1072         cmd.data_direction = CGC_DATA_NONE;
1073         break;
1074     default:
1075        return STATUS_INVALID_PARAMETER;
1076        break;
1077     }
1078
1079     io = ioctl(dev, CDROM_SEND_PACKET, &cmd);
1080
1081     if (pPacket->SenseInfoLength != 0)
1082     {
1083         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1084                &sense, pPacket->SenseInfoLength);
1085     }
1086
1087     pPacket->ScsiStatus = cmd.stat;
1088
1089     ret = CDROM_GetStatusCode(io);
1090 #endif
1091     return ret;
1092 }
1093
1094 /******************************************************************
1095  *              CDROM_ScsiPassThrough
1096  *
1097  *
1098  */
1099 static DWORD CDROM_ScsiPassThrough(int dev, PSCSI_PASS_THROUGH pPacket)
1100 {
1101     int ret = STATUS_NOT_SUPPORTED;
1102 #if defined(linux)
1103     struct cdrom_generic_command cmd;
1104     struct request_sense sense;
1105     int io;
1106
1107     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1108         return STATUS_BUFFER_TOO_SMALL;
1109
1110     if (pPacket->CdbLength > 12)
1111         return STATUS_INVALID_PARAMETER;
1112
1113     if (pPacket->SenseInfoLength > sizeof(sense))
1114         return STATUS_INVALID_PARAMETER;
1115
1116     memset(&cmd, 0, sizeof(cmd));
1117     memset(&sense, 0, sizeof(sense));
1118
1119     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1120
1121     if ( pPacket->DataBufferOffset > 0x1000 )
1122     {
1123         cmd.buffer     = (void*)pPacket->DataBufferOffset;
1124     }
1125     else
1126     {
1127         cmd.buffer     = ((void*)pPacket) + pPacket->DataBufferOffset;
1128     }
1129     cmd.buflen         = pPacket->DataTransferLength;
1130     cmd.sense          = &sense;
1131     cmd.quiet          = 0;
1132     cmd.timeout        = pPacket->TimeOutValue*HZ;
1133
1134     switch (pPacket->DataIn)
1135     {
1136     case SCSI_IOCTL_DATA_OUT:
1137         cmd.data_direction = CGC_DATA_WRITE;
1138         break;
1139     case SCSI_IOCTL_DATA_IN:
1140         cmd.data_direction = CGC_DATA_READ;
1141         break;
1142     case SCSI_IOCTL_DATA_UNSPECIFIED:
1143         cmd.data_direction = CGC_DATA_NONE;
1144         break;
1145     default:
1146        return STATUS_INVALID_PARAMETER;
1147        break;
1148     }
1149
1150     io = ioctl(dev, CDROM_SEND_PACKET, &cmd);
1151
1152     if (pPacket->SenseInfoLength != 0)
1153     {
1154         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1155                &sense, pPacket->SenseInfoLength);
1156     }
1157
1158     pPacket->ScsiStatus = cmd.stat;
1159
1160     ret = CDROM_GetStatusCode(io);
1161 #endif
1162     return ret;
1163 }
1164
1165 /******************************************************************
1166  *              CDROM_GetAddress
1167  * 
1168  * implements IOCTL_SCSI_GET_ADDRESS
1169  */
1170 static DWORD CDROM_GetAddress(int dev, SCSI_ADDRESS* address)
1171 {
1172     int portnum, targetid;
1173
1174     address->Length = sizeof(SCSI_ADDRESS);
1175     address->PathId = 0; /* bus number */
1176     address->Lun = 0;
1177     if ( ! CDROM_GetIdeInterface(dev, &portnum, &targetid))
1178         return STATUS_NOT_SUPPORTED;
1179
1180     address->PortNumber = portnum;
1181     address->TargetId = targetid;
1182     return 0;
1183 }
1184
1185 /******************************************************************
1186  *              CDROM_DeviceIoControl
1187  *
1188  *
1189  */
1190 BOOL CDROM_DeviceIoControl(DWORD clientID, HANDLE hDevice, DWORD dwIoControlCode, 
1191                            LPVOID lpInBuffer, DWORD nInBufferSize, 
1192                            LPVOID lpOutBuffer, DWORD nOutBufferSize,
1193                            LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
1194 {
1195     DWORD       sz;
1196     DWORD       error = 0;
1197     int         dev;
1198
1199     TRACE("%lx[%c] %lx %lx %ld %lx %ld %lx %lx\n", 
1200           (DWORD)hDevice, 'A' + LOWORD(clientID), dwIoControlCode, (DWORD)lpInBuffer, nInBufferSize, 
1201           (DWORD)lpOutBuffer, nOutBufferSize, (DWORD)lpBytesReturned, (DWORD)lpOverlapped);
1202
1203     if (lpBytesReturned) *lpBytesReturned = 0;
1204     if (lpOverlapped)
1205     {
1206         FIXME("Overlapped isn't implemented yet\n");
1207         SetLastError(STATUS_NOT_SUPPORTED);
1208         return FALSE;
1209     }
1210
1211     SetLastError(0);
1212     dev = CDROM_Open(hDevice, clientID);
1213     if (dev == -1)
1214     {
1215         CDROM_GetStatusCode(-1);
1216         return FALSE;
1217     }
1218
1219     switch (dwIoControlCode)
1220     {
1221     case IOCTL_STORAGE_CHECK_VERIFY:
1222     case IOCTL_CDROM_CHECK_VERIFY:
1223         sz = 0;
1224         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1225             error = STATUS_INVALID_PARAMETER;
1226         else error = CDROM_Verify(dev);
1227         break;
1228
1229 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
1230
1231 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
1232 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
1233
1234     case IOCTL_STORAGE_LOAD_MEDIA:
1235     case IOCTL_CDROM_LOAD_MEDIA:
1236         sz = 0;
1237         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1238             error = STATUS_INVALID_PARAMETER;
1239         else error = CDROM_SetTray(dev, FALSE);
1240         break;
1241      case IOCTL_STORAGE_EJECT_MEDIA:
1242         sz = 0;
1243         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1244             error = STATUS_INVALID_PARAMETER;
1245         else error = CDROM_SetTray(dev, TRUE);
1246         break;
1247
1248     case IOCTL_CDROM_MEDIA_REMOVAL:
1249     case IOCTL_DISK_MEDIA_REMOVAL:
1250     case IOCTL_STORAGE_MEDIA_REMOVAL:
1251     case IOCTL_STORAGE_EJECTION_CONTROL:
1252         /* FIXME the last ioctl:s is not the same as the two others... 
1253          * lockcount/owner should be handled */
1254         sz = 0;
1255         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1256         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) error = STATUS_BUFFER_TOO_SMALL;
1257         else error = CDROM_ControlEjection(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1258         break;
1259
1260 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
1261
1262     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1263         sz = sizeof(STORAGE_DEVICE_NUMBER);
1264         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1265         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1266         else error = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
1267         break;
1268
1269     case IOCTL_STORAGE_RESET_DEVICE:
1270         sz = 0;
1271         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1272             error = STATUS_INVALID_PARAMETER;
1273         else error = CDROM_ResetAudio(dev);
1274         break;
1275
1276     case IOCTL_CDROM_GET_CONTROL:
1277         sz = sizeof(CDROM_AUDIO_CONTROL);
1278         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1279         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1280         else error = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
1281         break;
1282
1283     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1284         sz = sizeof(DISK_GEOMETRY);
1285         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1286         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1287         else error = CDROM_GetDriveGeometry(dev, (DISK_GEOMETRY*)lpOutBuffer);
1288         break;
1289
1290     case IOCTL_CDROM_DISK_TYPE:
1291         sz = sizeof(CDROM_DISK_DATA);
1292         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1293         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1294         else error = CDROM_GetDiskData(dev, (CDROM_DISK_DATA*)lpOutBuffer);
1295         break;
1296
1297 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
1298
1299     case IOCTL_CDROM_READ_Q_CHANNEL:
1300         sz = sizeof(SUB_Q_CHANNEL_DATA);
1301         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT)) 
1302             error = STATUS_INVALID_PARAMETER;
1303         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1304         else error = CDROM_ReadQChannel(dev, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1305                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1306         break;
1307
1308     case IOCTL_CDROM_READ_TOC:
1309         sz = sizeof(CDROM_TOC);
1310         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1311         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1312         else error = CDROM_ReadTOC(dev, (CDROM_TOC*)lpOutBuffer);
1313         break;
1314
1315 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
1316
1317     case IOCTL_CDROM_PAUSE_AUDIO:
1318         sz = 0;
1319         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1320             error = STATUS_INVALID_PARAMETER;
1321         else error = CDROM_PauseAudio(dev);
1322         break;
1323     case IOCTL_CDROM_PLAY_AUDIO_MSF:
1324         sz = 0;
1325         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1326         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1327         else error = CDROM_PlayAudioMSF(dev, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1328         break;
1329     case IOCTL_CDROM_RESUME_AUDIO:
1330         sz = 0;
1331         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1332             error = STATUS_INVALID_PARAMETER;
1333         else error = CDROM_ResumeAudio(dev);
1334         break;
1335     case IOCTL_CDROM_SEEK_AUDIO_MSF:
1336         sz = 0;
1337         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1338         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1339         else error = CDROM_SeekAudioMSF(dev, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1340         break;
1341     case IOCTL_CDROM_STOP_AUDIO:
1342         sz = 0;
1343         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1344             error = STATUS_INVALID_PARAMETER;
1345         else error = CDROM_StopAudio(dev);
1346         break;
1347     case IOCTL_CDROM_GET_VOLUME:
1348         sz = sizeof(VOLUME_CONTROL);
1349         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1350         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1351         else error = CDROM_GetVolume(dev, (VOLUME_CONTROL*)lpOutBuffer);
1352         break;
1353     case IOCTL_CDROM_SET_VOLUME:
1354         sz = 0;
1355         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1356             error = STATUS_INVALID_PARAMETER;
1357         else error = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
1358         break;
1359     case IOCTL_CDROM_RAW_READ:
1360         sz = 0;
1361         if (nInBufferSize < sizeof(RAW_READ_INFO)) error = STATUS_INVALID_PARAMETER;
1362         else if (lpOutBuffer == NULL) error = STATUS_BUFFER_TOO_SMALL;
1363         else error = CDROM_RawRead(dev, (const RAW_READ_INFO*)lpInBuffer, 
1364                                    lpOutBuffer, nOutBufferSize, &sz);
1365         break;
1366     case IOCTL_SCSI_GET_ADDRESS:
1367         sz = sizeof(SCSI_ADDRESS);
1368         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1369         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1370         else error = CDROM_GetAddress(dev, (SCSI_ADDRESS*)lpOutBuffer);
1371         break;
1372     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
1373         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
1374         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1375         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) error = STATUS_BUFFER_TOO_SMALL;
1376         else error = CDROM_ScsiPassThroughDirect(dev, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1377         break;
1378     case IOCTL_SCSI_PASS_THROUGH:
1379         sz = sizeof(SCSI_PASS_THROUGH);
1380         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1381         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) error = STATUS_BUFFER_TOO_SMALL;
1382         else error = CDROM_ScsiPassThrough(dev, (PSCSI_PASS_THROUGH)lpOutBuffer);
1383         break;
1384
1385     default:
1386         FIXME("Unsupported IOCTL %lx\n", dwIoControlCode);
1387         sz = 0;
1388         error = STATUS_INVALID_PARAMETER;
1389         break;
1390     }
1391
1392     if (lpBytesReturned) *lpBytesReturned = sz;
1393     if (error) 
1394     {
1395         SetLastError(error);
1396         return FALSE;
1397     }
1398     CDROM_Close(clientID, dev);
1399     return TRUE;
1400 }