Added version information.
[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 #include "wine/port.h"
25
26 #include <errno.h>
27 #include <string.h>
28 #include <stdio.h>
29 #ifdef HAVE_IO_H
30 # include <io.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38
39 #ifdef HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42 #ifdef HAVE_SCSI_SG_H
43 # include <scsi/sg.h>
44 #endif
45 #ifdef HAVE_LINUX_MAJOR_H
46 # include <linux/major.h>
47 #endif
48 #ifdef HAVE_LINUX_HDREG_H
49 # include <linux/hdreg.h>
50 #endif
51 #ifdef HAVE_LINUX_PARAM_H
52 # include <linux/param.h>
53 #endif
54 #ifdef HAVE_LINUX_CDROM_H
55 # include <linux/cdrom.h>
56 #endif
57 #ifdef HAVE_LINUX_UCDROM_H
58 # include <linux/ucdrom.h>
59 #endif
60 #ifdef HAVE_SYS_CDIO_H
61 # include <sys/cdio.h>
62 #endif
63
64 #define NONAMELESSUNION
65 #define NONAMELESSSTRUCT
66 #include "winternl.h"
67 #include "winioctl.h"
68 #include "ntddstor.h"
69 #include "ntddcdrm.h"
70 #include "ntddscsi.h"
71 #include "drive.h"
72 #include "file.h"
73 #include "wine/debug.h"
74
75 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
76    lack the following constants. */
77
78 #ifndef CD_SECS
79   #define CD_SECS              60 /* seconds per minute */
80 #endif
81 #ifndef CD_FRAMES
82   #define CD_FRAMES            75 /* frames per second */
83 #endif
84
85 static const struct iocodexs
86 {
87   DWORD code;
88   const char *codex;
89 } iocodextable[] = {
90 {IOCTL_CDROM_UNLOAD_DRIVER, "IOCTL_CDROM_UNLOAD_DRIVER"},
91 {IOCTL_CDROM_READ_TOC, "IOCTL_CDROM_READ_TOC"},
92 {IOCTL_CDROM_GET_CONTROL, "IOCTL_CDROM_GET_CONTROL"},
93 {IOCTL_CDROM_PLAY_AUDIO_MSF, "IOCTL_CDROM_PLAY_AUDIO_MSF"},
94 {IOCTL_CDROM_SEEK_AUDIO_MSF, "IOCTL_CDROM_SEEK_AUDIO_MSF"},
95 {IOCTL_CDROM_STOP_AUDIO, "IOCTL_CDROM_STOP_AUDIO"},
96 {IOCTL_CDROM_PAUSE_AUDIO, "IOCTL_CDROM_PAUSE_AUDIO"},
97 {IOCTL_CDROM_RESUME_AUDIO, "IOCTL_CDROM_RESUME_AUDIO"},
98 {IOCTL_CDROM_GET_VOLUME, "IOCTL_CDROM_GET_VOLUME"},
99 {IOCTL_CDROM_SET_VOLUME, "IOCTL_CDROM_SET_VOLUME"},
100 {IOCTL_CDROM_READ_Q_CHANNEL, "IOCTL_CDROM_READ_Q_CHANNEL"},
101 {IOCTL_CDROM_GET_LAST_SESSION, "IOCTL_CDROM_GET_LAST_SESSION"},
102 {IOCTL_CDROM_RAW_READ, "IOCTL_CDROM_RAW_READ"},
103 {IOCTL_CDROM_DISK_TYPE, "IOCTL_CDROM_DISK_TYPE"},
104 {IOCTL_CDROM_GET_DRIVE_GEOMETRY, "IOCTL_CDROM_GET_DRIVE_GEOMETRY"},
105 {IOCTL_CDROM_CHECK_VERIFY, "IOCTL_CDROM_CHECK_VERIFY"},
106 {IOCTL_CDROM_MEDIA_REMOVAL, "IOCTL_CDROM_MEDIA_REMOVAL"},
107 {IOCTL_CDROM_EJECT_MEDIA, "IOCTL_CDROM_EJECT_MEDIA"},
108 {IOCTL_CDROM_LOAD_MEDIA, "IOCTL_CDROM_LOAD_MEDIA"},
109 {IOCTL_CDROM_RESERVE, "IOCTL_CDROM_RESERVE"},
110 {IOCTL_CDROM_RELEASE, "IOCTL_CDROM_RELEASE"},
111 {IOCTL_CDROM_FIND_NEW_DEVICES, "IOCTL_CDROM_FIND_NEW_DEVICES"}
112 };
113 static const char *iocodex(DWORD code)
114 {
115    int i;
116    static char buffer[25];
117    for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
118       if (code==iocodextable[i].code)
119          return iocodextable[i].codex;
120    sprintf(buffer, "IOCTL_CODE_%x", (int)code);
121    return buffer;
122 }
123
124 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
125
126 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
127 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
128 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
129 #define MSF_OF_FRAME(m,fr) {int f=(fr); ((UCHAR *)&(m))[2]=f%CD_FRAMES;f/=CD_FRAMES;((UCHAR *)&(m))[1]=f%CD_SECS;((UCHAR *)&(m))[0]=f/CD_SECS;}
130
131 static DWORD CDROM_ReadTOC(int, CDROM_TOC*);
132 static DWORD CDROM_GetStatusCode(int);
133
134
135 #ifdef linux
136
137 # ifndef IDE6_MAJOR
138 #  define IDE6_MAJOR 88
139 # endif
140 # ifndef IDE7_MAJOR
141 #  define IDE7_MAJOR 89
142 # endif
143
144 # ifdef CDROM_SEND_PACKET
145 /* structure for CDROM_PACKET_COMMAND ioctl */
146 /* not all Linux versions have all the fields, so we define the
147  * structure ourselves to make sure */
148 struct linux_cdrom_generic_command
149 {
150     unsigned char          cmd[CDROM_PACKET_SIZE];
151     unsigned char         *buffer;
152     unsigned int           buflen;
153     int                    stat;
154     struct request_sense  *sense;
155     unsigned char          data_direction;
156     int                    quiet;
157     int                    timeout;
158     void                  *reserved[1];
159 };
160 # endif  /* CDROM_SEND_PACKET */
161
162 #endif  /* linux */
163
164 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
165  * this should be removed when a proper device interface is implemented
166  * 
167  * (WS) We need this to keep track of current position and to safely
168  * detect media changes. Besides this should provide a great speed up
169  * for toc inquiries.
170  */
171 struct cdrom_cache {
172     int fd;
173     int count;
174     char toc_good; /* if false, will reread TOC from disk */
175     CDROM_TOC toc;
176     SUB_Q_CURRENT_POSITION CurrentPosition;
177 };
178 static struct cdrom_cache cdrom_cache[26];
179
180 /* Proposed media change function: not really needed at this time */
181 /* This is a 1 or 0 type of function */
182 #if 0
183 static int CDROM_MediaChanged(int dev)
184 {
185    int i;
186
187    struct cdrom_tochdr  hdr;
188    struct cdrom_tocentry entry;
189
190    if (dev < 0 || dev >= 26)
191       return 0;
192    if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
193       return 0;
194
195    if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
196       return 1;
197
198    for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
199    {
200       if (i == hdr.cdth_trk1 + 1)
201       {
202          entry.cdte_track = CDROM_LEADOUT;
203       } else {
204          entry.cdte_track = i;
205       }
206       entry.cdte_format = CDROM_MSF;
207       if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
208          return 0;
209       if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
210                               sizeof(struct cdrom_tocentry)) )
211          return 1;
212    }
213    return 0;
214 }
215 #endif
216
217 /******************************************************************
218  *              CDROM_SyncCache                          [internal]
219  *
220  * Read the TOC in and store it in the cdrom_cache structure.
221  * Further requests for the TOC will be copied from the cache
222  * unless certain events like disk ejection is detected, in which
223  * case the cache will be cleared, causing it to be resynced.
224  *
225  */
226 static int CDROM_SyncCache(int dev)
227 {
228    int i, io = 0, tsz;
229 #ifdef linux
230    struct cdrom_tochdr          hdr;
231    struct cdrom_tocentry        entry;
232 #elif defined(__FreeBSD__) || defined(__NetBSD__)
233    struct ioc_toc_header        hdr;
234    struct ioc_read_toc_entry    entry;
235    struct cd_toc_entry         toc_buffer;
236 #endif
237    CDROM_TOC *toc = &cdrom_cache[dev].toc;
238    cdrom_cache[dev].toc_good = 0;
239
240 #ifdef linux
241
242    io = ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr);
243    if (io == -1)
244    {
245       WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
246       goto end;
247    }
248    
249    TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
250
251    toc->FirstTrack = hdr.cdth_trk0;
252    toc->LastTrack  = hdr.cdth_trk1;
253    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
254        + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
255    toc->Length[0] = tsz >> 8;
256    toc->Length[1] = tsz;
257
258    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
259    {
260      if (i == toc->LastTrack + 1)
261        entry.cdte_track = CDROM_LEADOUT;
262      else 
263        entry.cdte_track = i;
264      entry.cdte_format = CDROM_MSF;
265      io = ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry);
266      if (io == -1) {
267        WARN("error read entry (%s)\n", strerror(errno));
268        goto end;
269      }
270      toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
271      toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
272      /* marking last track with leadout value as index */
273      toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
274      toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
275      toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
276      toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
277      toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
278     }
279     cdrom_cache[dev].toc_good = 1;
280     io = 0;
281 #elif defined(__FreeBSD__) || defined(__NetBSD__)
282
283     io = ioctl(cdrom_cache[dev].fd, CDIOREADTOCHEADER, &hdr);
284     if (io == -1)
285     {
286         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
287         goto end;
288     }
289     toc->FirstTrack = hdr.starting_track;
290     toc->LastTrack  = hdr.ending_track;
291     tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
292         + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
293     toc->Length[0] = tsz >> 8;
294     toc->Length[1] = tsz;
295
296     TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
297
298     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
299     {
300         if (i == toc->LastTrack + 1)
301         {
302 #define LEADOUT 0xaa
303             entry.starting_track = LEADOUT;
304         } else {
305             entry.starting_track = i;
306         }
307         memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
308         entry.address_format = CD_MSF_FORMAT;
309         entry.data_len = sizeof(toc_buffer);
310         entry.data = &toc_buffer;
311         io = ioctl(cdrom_cache[dev].fd, CDIOREADTOCENTRYS, &entry);
312         if (io == -1) {
313             WARN("error read entry (%s)\n", strerror(errno));
314             goto end;
315         }
316         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
317         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
318         /* marking last track with leadout value as index */
319         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
320         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
321         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
322         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
323         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
324     }
325     cdrom_cache[dev].toc_good = 1;
326     io = 0;
327 #else
328     return STATUS_NOT_SUPPORTED;
329 #endif
330 end:
331     return CDROM_GetStatusCode(io);
332 }
333
334 static void CDROM_ClearCacheEntry(int dev)
335 {
336   cdrom_cache[dev].toc_good = 0;
337 }
338
339
340
341 /******************************************************************
342  *              CDROM_GetIdeInterface
343  *
344  * Determines the ide interface (the number after the ide), and the
345  * number of the device on that interface for ide cdroms.
346  * Returns false if the info could not be get
347  *
348  * NOTE: this function is used in CDROM_InitRegistry and CDROM_GetAddress
349  */
350 static int CDROM_GetIdeInterface(int fd, int* iface, int* device)
351 {
352 #if defined(linux)
353     {
354         struct stat st;
355 #ifdef SG_EMULATED_HOST
356         if (ioctl(fd, SG_EMULATED_HOST) != -1) {
357             FIXME("not implemented for true scsi drives\n");
358             return 0;
359         }
360 #endif
361         if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) {
362             FIXME("cdrom not a block device!!!\n");
363             return 0;
364         }
365         switch (major(st.st_rdev)) {
366             case IDE0_MAJOR: *iface = 0; break;
367             case IDE1_MAJOR: *iface = 1; break;
368             case IDE2_MAJOR: *iface = 2; break;
369             case IDE3_MAJOR: *iface = 3; break;
370             case IDE4_MAJOR: *iface = 4; break;
371             case IDE5_MAJOR: *iface = 5; break;
372             case IDE6_MAJOR: *iface = 6; break;
373             case IDE7_MAJOR: *iface = 7; break;
374             case SCSI_CDROM_MAJOR: *iface = 11; break;
375             default:
376                 FIXME("CD-ROM device with major ID %d not supported\n", major(st.st_rdev));
377                 break;
378         }
379         *device = (minor(st.st_rdev) == 63 ? 1 : 0);
380         return 1;
381     }
382 #elif defined(__FreeBSD__) || defined(__NetBSD__)
383     FIXME("not implemented for BSD\n");
384     return 0;
385 #else
386     FIXME("not implemented for nonlinux\n");
387     return 0;
388 #endif
389 }
390
391
392 /******************************************************************
393  *              CDROM_InitRegistry
394  *
395  * Initializes registry to contain scsi info about the cdrom in NT.
396  * All devices (even not real scsi ones) have this info in NT.
397  * TODO: for now it only works for non scsi devices
398  * NOTE: programs usually read these registry entries after sending the
399  *       IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
400  */
401 void CDROM_InitRegistry(int fd)
402 {
403     int portnum, targetid;
404     OBJECT_ATTRIBUTES attr;
405     UNICODE_STRING nameW;
406     WCHAR dataW[50];
407     DWORD lenW;
408     char buffer[40];
409     DWORD value;
410     const char *data;
411     HKEY scsiKey;
412     HKEY portKey;
413     HKEY busKey;
414     HKEY targetKey;
415     DWORD disp;
416
417     attr.Length = sizeof(attr);
418     attr.RootDirectory = 0;
419     attr.ObjectName = &nameW;
420     attr.Attributes = 0;
421     attr.SecurityDescriptor = NULL;
422     attr.SecurityQualityOfService = NULL;
423
424     if ( ! CDROM_GetIdeInterface(fd, &portnum, &targetid))
425         return;
426
427     /* Ensure there is Scsi key */
428     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\HARDWARE\\DEVICEMAP\\Scsi" ) ||
429         NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
430                      NULL, REG_OPTION_VOLATILE, &disp ))
431     {
432         ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
433         return;
434     }
435     RtlFreeUnicodeString( &nameW );
436
437     snprintf(buffer,40,"Scsi Port %d",portnum);
438     attr.RootDirectory = scsiKey;
439     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
440         NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
441                      NULL, REG_OPTION_VOLATILE, &disp ))
442     {
443         ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
444         return;
445     }
446     RtlFreeUnicodeString( &nameW );
447
448     RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
449     data = "atapi";
450     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
451     NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
452     RtlFreeUnicodeString( &nameW );
453     value = 10;
454     RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
455     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
456     RtlFreeUnicodeString( &nameW );
457     value = 0;
458 #ifdef HDIO_GET_DMA
459     {
460         int dma;
461         if (ioctl(fd,HDIO_GET_DMA, &dma) != -1) {
462             value = dma;
463             TRACE("setting dma to %lx\n", value);
464         }
465     }
466 #endif
467     RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
468     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
469     RtlFreeUnicodeString( &nameW );
470
471     attr.RootDirectory = portKey;
472     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Scsi Bus 0" ) ||
473         NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
474                      NULL, REG_OPTION_VOLATILE, &disp ))
475     {
476         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
477         return;
478     }
479     RtlFreeUnicodeString( &nameW );
480
481     attr.RootDirectory = busKey;
482     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
483         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
484                      NULL, REG_OPTION_VOLATILE, &disp ))
485     {
486         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
487         return;
488     }
489     RtlFreeUnicodeString( &nameW );
490     NtClose( targetKey );
491
492     snprintf(buffer,40,"Target Id %d", targetid);
493     attr.RootDirectory = busKey;
494     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
495         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
496                      NULL, REG_OPTION_VOLATILE, &disp ))
497     {
498         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
499         return;
500     }
501     RtlFreeUnicodeString( &nameW );
502
503     RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
504     data = "CdRomPeripheral";
505     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
506     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
507     RtlFreeUnicodeString( &nameW );
508     /* FIXME - maybe read the real identifier?? */
509     RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
510     data = "Wine CDROM";
511     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
512     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
513     RtlFreeUnicodeString( &nameW );
514     /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */
515     RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
516     data = "Cdrom0";
517     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
518     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
519     RtlFreeUnicodeString( &nameW );
520
521     NtClose( targetKey );
522     NtClose( busKey );
523     NtClose( portKey );
524     NtClose( scsiKey );
525 }
526
527
528 /******************************************************************
529  *              CDROM_Open
530  *
531  *
532  */
533 static int CDROM_Open(HANDLE hDevice, DWORD clientID)
534 {
535     int dev = LOWORD(clientID);
536
537     if (dev >= 26) return -1;
538
539     if (!cdrom_cache[dev].count)
540     {
541         char root[4];
542         const char *device;
543
544         strcpy(root, "A:\\");
545         root[0] += dev;
546         if (GetDriveTypeA(root) != DRIVE_CDROM) return -1;
547         if (!(device = DRIVE_GetDevice(dev))) return -1;
548         cdrom_cache[dev].fd = open(device, O_RDONLY|O_NONBLOCK);
549         if (cdrom_cache[dev].fd == -1)
550         {
551             FIXME("Can't open configured CD-ROM drive at %s (device %s): %s\n", root, DRIVE_GetDevice(dev), strerror(errno));
552             return -1;
553         }
554     }
555     cdrom_cache[dev].count++;
556     TRACE("%d, %d, %d\n", dev, cdrom_cache[dev].fd, cdrom_cache[dev].count);
557     /* return cdrom_cache[dev].fd; */
558     return dev; /* can still find fd by using dev */
559 }
560
561 /******************************************************************
562  *              CDROM_Close
563  *
564  *
565  */
566 static void CDROM_Close(DWORD clientID)
567 {
568     int dev = LOWORD(clientID);
569
570     if (dev >= 26 /*|| fd != cdrom_cache[dev].fd*/) FIXME("how come\n");
571     if (--cdrom_cache[dev].count == 0) {
572       close(cdrom_cache[dev].fd);
573       cdrom_cache[dev].fd = -1;
574     }
575 }
576
577 /******************************************************************
578  *              CDROM_GetStatusCode
579  *
580  *
581  */
582 static DWORD CDROM_GetStatusCode(int io)
583 {
584     if (io == 0) return STATUS_SUCCESS;
585     switch (errno)
586     {
587     case EIO:
588 #ifdef ENOMEDIUM
589     case ENOMEDIUM:
590 #endif
591             return STATUS_NO_MEDIA_IN_DEVICE;
592     case EPERM:
593             return STATUS_ACCESS_DENIED;
594     case EINVAL:
595             return STATUS_INVALID_PARAMETER;
596     /* case EBADF: Bad file descriptor */
597     }
598     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
599     return STATUS_IO_DEVICE_ERROR;
600 }
601
602 static DWORD CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
603 {
604     cac->LbaFormat = 0; /* FIXME */
605     cac->LogicalBlocksPerSecond = 1; /* FIXME */
606     return  STATUS_NOT_SUPPORTED;
607 }
608
609 static DWORD CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
610 {
611     return STATUS_NOT_SUPPORTED;
612 }
613
614 static DWORD CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
615 {
616   CDROM_TOC   toc;
617   DWORD ret = 0;
618   int fsize=0;
619
620   if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
621
622   fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
623         - FRAME_OF_TOC(toc, 1); /* Total size in frames */
624   
625   dg->Cylinders.s.LowPart = fsize / (64 * 32); 
626   dg->Cylinders.s.HighPart = 0; 
627   dg->MediaType = RemovableMedia;  
628   dg->TracksPerCylinder = 64; 
629   dg->SectorsPerTrack = 32;  
630   dg->BytesPerSector= 2048; 
631   return ret;
632 }
633
634 /**************************************************************************
635  *                              CDROM_Reset                     [internal]
636  */
637 static DWORD CDROM_ResetAudio(int dev)
638 {
639 #if defined(linux)
640     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMRESET));
641 #elif defined(__FreeBSD__) || defined(__NetBSD__)
642     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCRESET, NULL));
643 #else
644     return STATUS_NOT_SUPPORTED;
645 #endif
646 }
647
648 /******************************************************************
649  *              CDROM_SetTray
650  *
651  *
652  */
653 static DWORD CDROM_SetTray(int dev, BOOL doEject)
654 {
655 #if defined(linux)
656     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
657 #elif defined(__FreeBSD__) || defined(__NetBSD__)
658     return CDROM_GetStatusCode((ioctl(cdrom_cache[dev].fd, CDIOCALLOW, NULL)) ||
659                                (ioctl(cdrom_cache[dev].fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
660                                (ioctl(cdrom_cache[dev].fd, CDIOCPREVENT, NULL)));
661 #else
662     return STATUS_NOT_SUPPORTED;
663 #endif
664 }
665
666 /******************************************************************
667  *              CDROM_ControlEjection
668  *
669  *
670  */
671 static DWORD CDROM_ControlEjection(int dev, const PREVENT_MEDIA_REMOVAL* rmv)
672 {
673 #if defined(linux)
674     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
675 #elif defined(__FreeBSD__) || defined(__NetBSD__)
676     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
677 #else
678     return STATUS_NOT_SUPPORTED;
679 #endif
680 }
681
682 /******************************************************************
683  *              CDROM_ReadTOC
684  *
685  *
686  */
687 static DWORD CDROM_ReadTOC(int dev, CDROM_TOC* toc)
688 {
689     DWORD       ret = STATUS_NOT_SUPPORTED;
690
691     if (dev < 0 || dev >= 26)
692        return STATUS_INVALID_PARAMETER;
693     if ( !cdrom_cache[dev].toc_good ) {
694        ret = CDROM_SyncCache(dev);
695        if ( ret )
696           return ret;
697     }
698     *toc = cdrom_cache[dev].toc;
699     return STATUS_SUCCESS;
700 }
701
702 /******************************************************************
703  *              CDROM_GetDiskData
704  *
705  *
706  */
707 static DWORD CDROM_GetDiskData(int dev, CDROM_DISK_DATA* data)
708 {
709     CDROM_TOC   toc;
710     DWORD       ret;
711     int         i;
712
713     if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
714     data->DiskData = 0;
715     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
716         if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
717             data->DiskData |= CDROM_DISK_DATA_TRACK;
718         else
719             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
720     }
721     return STATUS_SUCCESS;
722 }
723
724 /******************************************************************
725  *              CDROM_ReadQChannel
726  *
727  *
728  */
729 static DWORD CDROM_ReadQChannel(int dev, const CDROM_SUB_Q_DATA_FORMAT* fmt,
730                                 SUB_Q_CHANNEL_DATA* data)
731 {
732     DWORD               ret = STATUS_NOT_SUPPORTED;
733 #ifdef linux
734     unsigned            size;
735     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
736     int                 io;
737     struct cdrom_subchnl        sc;
738     sc.cdsc_format = CDROM_MSF;
739
740     io = ioctl(cdrom_cache[dev].fd, CDROMSUBCHNL, &sc);
741     if (io == -1)
742     {
743         TRACE("opened or no_media (%s)!\n", strerror(errno));
744         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
745         CDROM_ClearCacheEntry(dev);
746         goto end;
747     }
748
749     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
750
751     switch (sc.cdsc_audiostatus) {
752     case CDROM_AUDIO_INVALID:
753         CDROM_ClearCacheEntry(dev);
754         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
755         break;
756     case CDROM_AUDIO_NO_STATUS:
757         CDROM_ClearCacheEntry(dev);
758         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
759         break;
760     case CDROM_AUDIO_PLAY:
761         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
762         break;
763     case CDROM_AUDIO_PAUSED:
764         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
765         break;
766     case CDROM_AUDIO_COMPLETED:
767         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
768         break;
769     case CDROM_AUDIO_ERROR:
770         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
771         break;
772     default:
773         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
774         break;
775     }
776     switch (fmt->Format)
777     {
778     case IOCTL_CDROM_CURRENT_POSITION:
779         size = sizeof(SUB_Q_CURRENT_POSITION);
780         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
781           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
782           data->CurrentPosition.Control = sc.cdsc_ctrl; 
783           data->CurrentPosition.ADR = sc.cdsc_adr; 
784           data->CurrentPosition.TrackNumber = sc.cdsc_trk; 
785           data->CurrentPosition.IndexNumber = sc.cdsc_ind; 
786
787           data->CurrentPosition.AbsoluteAddress[0] = 0; 
788           data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; 
789           data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
790           data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
791  
792           data->CurrentPosition.TrackRelativeAddress[0] = 0; 
793           data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; 
794           data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
795           data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
796
797           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
798         }
799         else /* not playing */
800         {
801           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
802           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
803         }
804         break;
805     case IOCTL_CDROM_MEDIA_CATALOG:
806         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
807         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
808         {
809             struct cdrom_mcn mcn;
810             if ((io = ioctl(cdrom_cache[dev].fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
811
812             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
813             data->MediaCatalog.Mcval = 0; /* FIXME */
814             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
815             data->MediaCatalog.MediaCatalog[14] = 0;
816         }
817         break;
818     case IOCTL_CDROM_TRACK_ISRC:
819         size = sizeof(SUB_Q_CURRENT_POSITION);
820         FIXME("TrackIsrc: NIY on linux\n");
821         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
822         data->TrackIsrc.Tcval = 0;
823         io = 0;
824         break;
825     }
826
827  end:
828     ret = CDROM_GetStatusCode(io);
829 #elif defined(__FreeBSD__) || defined(__NetBSD__)
830     unsigned            size;
831     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
832     int                 io;
833     struct ioc_read_subchannel  read_sc;
834     struct cd_sub_channel_info  sc;
835
836     read_sc.address_format = CD_MSF_FORMAT;
837     read_sc.track          = 0;
838     read_sc.data_len       = sizeof(sc);
839     read_sc.data           = &sc;
840     switch (fmt->Format)
841     {
842     case IOCTL_CDROM_CURRENT_POSITION:
843         read_sc.data_format    = CD_CURRENT_POSITION;
844         break;
845     case IOCTL_CDROM_MEDIA_CATALOG:
846         read_sc.data_format    = CD_MEDIA_CATALOG;
847         break;
848     case IOCTL_CDROM_TRACK_ISRC:
849         read_sc.data_format    = CD_TRACK_INFO;
850         sc.what.track_info.track_number = data->TrackIsrc.Track;
851         break;
852     }
853     io = ioctl(cdrom_cache[dev].fd, CDIOCREADSUBCHANNEL, &read_sc);
854     if (io == -1)
855     {
856         TRACE("opened or no_media (%s)!\n", strerror(errno));
857         CDROM_ClearCacheEntry(dev);
858         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
859         goto end;
860     }
861
862     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
863
864     switch (sc.header.audio_status) {
865     case CD_AS_AUDIO_INVALID:
866         CDROM_ClearCacheEntry(dev);
867         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
868         break;
869     case CD_AS_NO_STATUS:
870         CDROM_ClearCacheEntry(dev);
871         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
872         break;
873     case CD_AS_PLAY_IN_PROGRESS:
874         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
875         break;
876     case CD_AS_PLAY_PAUSED:
877         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
878         break;
879     case CD_AS_PLAY_COMPLETED:
880         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
881         break;
882     case CD_AS_PLAY_ERROR:
883         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
884         break;
885     default:
886         TRACE("status=%02X !\n", sc.header.audio_status);
887     }
888     switch (fmt->Format)
889     {
890     case IOCTL_CDROM_CURRENT_POSITION:
891         size = sizeof(SUB_Q_CURRENT_POSITION);
892         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
893           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
894           data->CurrentPosition.Control = sc.what.position.control;
895           data->CurrentPosition.ADR = sc.what.position.addr_type;
896           data->CurrentPosition.TrackNumber = sc.what.position.track_number;
897           data->CurrentPosition.IndexNumber = sc.what.position.index_number;
898
899           data->CurrentPosition.AbsoluteAddress[0] = 0;
900           data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
901           data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
902           data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
903           data->CurrentPosition.TrackRelativeAddress[0] = 0;
904           data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
905           data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
906           data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
907           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
908         }
909         else { /* not playing */
910           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
911           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
912         }
913         break;
914     case IOCTL_CDROM_MEDIA_CATALOG:
915         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
916         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
917         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
918         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
919         break;
920     case IOCTL_CDROM_TRACK_ISRC:
921         size = sizeof(SUB_Q_CURRENT_POSITION);
922         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
923         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
924         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
925         break;
926     }
927
928  end:
929     ret = CDROM_GetStatusCode(io);
930 #endif
931     return ret;
932 }
933
934 /******************************************************************
935  *              CDROM_Verify
936  *
937  *
938  */
939 static DWORD CDROM_Verify(int dev)
940 {
941     /* quick implementation */
942     CDROM_SUB_Q_DATA_FORMAT     fmt;
943     SUB_Q_CHANNEL_DATA          data;
944
945     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
946     return CDROM_ReadQChannel(dev, &fmt, &data) ? 1 : 0;
947 }
948
949 /******************************************************************
950  *              CDROM_PlayAudioMSF
951  *
952  *
953  */
954 static DWORD CDROM_PlayAudioMSF(int dev, const CDROM_PLAY_AUDIO_MSF* audio_msf)
955 {
956     DWORD       ret = STATUS_NOT_SUPPORTED;
957 #ifdef linux
958     struct      cdrom_msf       msf;
959     int         io;
960
961     msf.cdmsf_min0   = audio_msf->StartingM;
962     msf.cdmsf_sec0   = audio_msf->StartingS;
963     msf.cdmsf_frame0 = audio_msf->StartingF;
964     msf.cdmsf_min1   = audio_msf->EndingM;
965     msf.cdmsf_sec1   = audio_msf->EndingS;
966     msf.cdmsf_frame1 = audio_msf->EndingF;
967
968     io = ioctl(cdrom_cache[dev].fd, CDROMSTART);
969     if (io == -1)
970     {
971         WARN("motor doesn't start !\n");
972         goto end;
973     }
974     io = ioctl(cdrom_cache[dev].fd, CDROMPLAYMSF, &msf);
975     if (io == -1)
976     {
977         WARN("device doesn't play !\n");
978         goto end;
979     }
980     TRACE("msf = %d:%d:%d %d:%d:%d\n",
981           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
982           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
983  end:
984     ret = CDROM_GetStatusCode(io);
985 #elif defined(__FreeBSD__) || defined(__NetBSD__)
986     struct      ioc_play_msf    msf;
987     int         io;
988
989     msf.start_m      = audio_msf->StartingM;
990     msf.start_s      = audio_msf->StartingS;
991     msf.start_f      = audio_msf->StartingF;
992     msf.end_m        = audio_msf->EndingM;
993     msf.end_s        = audio_msf->EndingS;
994     msf.end_f        = audio_msf->EndingF;
995
996     io = ioctl(cdrom_cache[dev].fd, CDIOCSTART, NULL);
997     if (io == -1)
998     {
999         WARN("motor doesn't start !\n");
1000         goto end;
1001     }
1002     io = ioctl(cdrom_cache[dev].fd, CDIOCPLAYMSF, &msf);
1003     if (io == -1)
1004     {
1005         WARN("device doesn't play !\n");
1006         goto end;
1007     }
1008     TRACE("msf = %d:%d:%d %d:%d:%d\n",
1009           msf.start_m, msf.start_s, msf.start_f,
1010           msf.end_m,   msf.end_s,   msf.end_f);
1011 end:
1012     ret = CDROM_GetStatusCode(io);
1013 #endif
1014     return ret;
1015 }
1016
1017 /******************************************************************
1018  *              CDROM_SeekAudioMSF
1019  *
1020  *
1021  */
1022 static DWORD CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1023 {
1024     CDROM_TOC toc;
1025     int i, io, frame;
1026     SUB_Q_CURRENT_POSITION *cp;
1027 #if defined(linux)
1028     struct cdrom_msf0   msf;
1029     struct cdrom_subchnl sc;
1030 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1031     struct ioc_play_msf msf;
1032     struct ioc_read_subchannel  read_sc;
1033     struct cd_sub_channel_info  sc;
1034     int final_frame;
1035 #endif
1036
1037     /* Use the information on the TOC to compute the new current
1038      * position, which is shadowed on the cache. [Portable]. */
1039     frame = FRAME_OF_MSF(*audio_msf);
1040     cp = &cdrom_cache[dev].CurrentPosition;
1041     if ((io = CDROM_ReadTOC(dev, &toc)) != 0) return io;
1042      
1043     for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1044       if (FRAME_OF_TOC(toc,i)>frame) break;
1045     if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1046       return STATUS_INVALID_PARAMETER;
1047     i--;
1048     cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION; 
1049     cp->Control = toc.TrackData[i-toc.FirstTrack].Control; 
1050     cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; 
1051     cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1052     cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1053     cp->AbsoluteAddress[0] = 0; 
1054     cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1055     cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1056     cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1057     frame -= FRAME_OF_TOC(toc,i);
1058     cp->TrackRelativeAddress[0] = 0;
1059     MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
1060
1061     /* If playing, then issue a seek command, otherwise do nothing */
1062 #ifdef linux
1063     sc.cdsc_format = CDROM_MSF;
1064
1065     io = ioctl(cdrom_cache[dev].fd, CDROMSUBCHNL, &sc);
1066     if (io == -1)
1067     {
1068         TRACE("opened or no_media (%s)!\n", strerror(errno));
1069         CDROM_ClearCacheEntry(dev);
1070         return CDROM_GetStatusCode(io);
1071     }
1072     if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1073     {
1074       msf.minute = audio_msf->M;
1075       msf.second = audio_msf->S;
1076       msf.frame  = audio_msf->F;
1077       return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMSEEK, &msf));
1078     }
1079     return STATUS_SUCCESS;
1080 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1081     read_sc.address_format = CD_MSF_FORMAT;
1082     read_sc.track          = 0;
1083     read_sc.data_len       = sizeof(sc);
1084     read_sc.data           = &sc;
1085     read_sc.data_format    = CD_CURRENT_POSITION;
1086
1087     io = ioctl(cdrom_cache[dev].fd, CDIOCREADSUBCHANNEL, &read_sc);
1088     if (io == -1)
1089     {
1090         TRACE("opened or no_media (%s)!\n", strerror(errno));
1091         CDROM_ClearCacheEntry(dev);
1092         return CDROM_GetStatusCode(io);
1093     }
1094     if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) 
1095     {
1096
1097       msf.start_m      = audio_msf->M;
1098       msf.start_s      = audio_msf->S;
1099       msf.start_f      = audio_msf->F;
1100       final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1101       MSF_OF_FRAME(msf.end_m, final_frame);
1102
1103       return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCPLAYMSF, &msf));
1104     }
1105     return STATUS_SUCCESS;
1106 #else
1107     return STATUS_NOT_SUPPORTED;
1108 #endif
1109 }
1110
1111 /******************************************************************
1112  *              CDROM_PauseAudio
1113  *
1114  *
1115  */
1116 static DWORD CDROM_PauseAudio(int dev)
1117 {
1118 #if defined(linux)
1119     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMPAUSE));
1120 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1121     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCPAUSE, NULL));
1122 #else
1123     return STATUS_NOT_SUPPORTED;
1124 #endif
1125 }
1126
1127 /******************************************************************
1128  *              CDROM_ResumeAudio
1129  *
1130  *
1131  */
1132 static DWORD CDROM_ResumeAudio(int dev)
1133 {
1134 #if defined(linux)
1135     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMRESUME));
1136 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1137     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCRESUME, NULL));
1138 #else
1139     return STATUS_NOT_SUPPORTED;
1140 #endif
1141 }
1142
1143 /******************************************************************
1144  *              CDROM_StopAudio
1145  *
1146  *
1147  */
1148 static DWORD CDROM_StopAudio(int dev)
1149 {
1150 #if defined(linux)
1151     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMSTOP));
1152 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1153     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCSTOP, NULL));
1154 #else
1155     return STATUS_NOT_SUPPORTED;
1156 #endif
1157 }
1158
1159 /******************************************************************
1160  *              CDROM_GetVolume
1161  *
1162  *
1163  */
1164 static DWORD CDROM_GetVolume(int dev, VOLUME_CONTROL* vc)
1165 {
1166 #if defined(linux)
1167     struct cdrom_volctrl volc;
1168     int io;
1169
1170     io = ioctl(cdrom_cache[dev].fd, CDROMVOLREAD, &volc);
1171     if (io != -1)
1172     {
1173         vc->PortVolume[0] = volc.channel0;
1174         vc->PortVolume[1] = volc.channel1;
1175         vc->PortVolume[2] = volc.channel2;
1176         vc->PortVolume[3] = volc.channel3;
1177     }
1178     return CDROM_GetStatusCode(io);
1179 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1180     struct  ioc_vol     volc;
1181     int io;
1182
1183     io = ioctl(cdrom_cache[dev].fd, CDIOCGETVOL, &volc);
1184     if (io != -1)
1185     {
1186         vc->PortVolume[0] = volc.vol[0];
1187         vc->PortVolume[1] = volc.vol[1];
1188         vc->PortVolume[2] = volc.vol[2];
1189         vc->PortVolume[3] = volc.vol[3];
1190     }
1191     return CDROM_GetStatusCode(io);
1192 #else
1193     return STATUS_NOT_SUPPORTED;
1194 #endif
1195 }
1196
1197 /******************************************************************
1198  *              CDROM_SetVolume
1199  *
1200  *
1201  */
1202 static DWORD CDROM_SetVolume(int dev, const VOLUME_CONTROL* vc)
1203 {
1204 #if defined(linux)
1205     struct cdrom_volctrl volc;
1206
1207     volc.channel0 = vc->PortVolume[0];
1208     volc.channel1 = vc->PortVolume[1];
1209     volc.channel2 = vc->PortVolume[2];
1210     volc.channel3 = vc->PortVolume[3];
1211
1212     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMVOLCTRL, &volc));
1213 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1214     struct  ioc_vol     volc;
1215
1216     volc.vol[0] = vc->PortVolume[0];
1217     volc.vol[1] = vc->PortVolume[1];
1218     volc.vol[2] = vc->PortVolume[2];
1219     volc.vol[3] = vc->PortVolume[3];
1220
1221     return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCSETVOL, &volc));
1222 #else
1223     return STATUS_NOT_SUPPORTED;
1224 #endif
1225 }
1226
1227 /******************************************************************
1228  *              CDROM_RawRead
1229  *
1230  *
1231  */
1232 static DWORD CDROM_RawRead(int dev, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1233 {
1234     int         ret = STATUS_NOT_SUPPORTED;
1235     int         io = -1;
1236     DWORD       sectSize;
1237
1238     switch (raw->TrackMode)
1239     {
1240     case YellowMode2:   sectSize = 2336;        break;
1241     case XAForm2:       sectSize = 2328;        break;
1242     case CDDA:          sectSize = 2352;        break;
1243     default:    return STATUS_INVALID_PARAMETER;
1244     }
1245     if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
1246     /* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
1247      * even if a larger size if read...
1248      */
1249 #if defined(linux)
1250     {
1251         struct cdrom_read       cdr;
1252         struct cdrom_read_audio cdra;
1253
1254         switch (raw->TrackMode)
1255         {
1256         case YellowMode2:
1257             if (raw->DiskOffset.s.HighPart) FIXME("Unsupported value\n");
1258             cdr.cdread_lba = raw->DiskOffset.s.LowPart; /* FIXME ? */
1259             cdr.cdread_bufaddr = buffer;
1260             cdr.cdread_buflen = raw->SectorCount * sectSize;
1261             io = ioctl(cdrom_cache[dev].fd, CDROMREADMODE2, &cdr);
1262             break;
1263         case XAForm2:
1264             FIXME("XAForm2: NIY\n");
1265             return ret;
1266         case CDDA:
1267             /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
1268              * between by NT2K box and this... should check on the same drive...
1269              * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
1270              * (linux/NT).
1271              * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
1272              * talking of 0.2 ms of sound
1273              */
1274             /* 2048 = 2 ** 11 */
1275             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1276             cdra.addr.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1277                 (raw->DiskOffset.s.HighPart << (32 - 11))) - 1;
1278             FIXME("reading at %u\n", cdra.addr.lba);
1279             cdra.addr_format = CDROM_LBA;
1280             cdra.nframes = raw->SectorCount;
1281             cdra.buf = buffer;
1282             io = ioctl(cdrom_cache[dev].fd, CDROMREADAUDIO, &cdra);
1283             break;
1284         default:
1285             FIXME("NIY: %d\n", raw->TrackMode);
1286             return ret;
1287         }
1288     }
1289 #elif defined(__FreeBSD__)
1290     {
1291         struct ioc_read_audio   ira;
1292
1293         switch (raw->TrackMode)
1294         {
1295         case YellowMode2:
1296             FIXME("YellowMode2: NIY\n");
1297             return ret;
1298         case XAForm2:
1299             FIXME("XAForm2: NIY\n");
1300             return ret;
1301         case CDDA:
1302             /* 2048 = 2 ** 11 */
1303             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1304             ira.address.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1305                 raw->DiskOffset.s.HighPart << (32 - 11)) - 1;
1306             ira.address_format = CD_LBA_FORMAT;
1307             ira.nframes = raw->SectorCount;
1308             ira.buffer = buffer;
1309             io = ioctl(cdrom_cache[dev].fd, CDIOCREADAUDIO, &ira);
1310             break;
1311         }
1312     }
1313 #elif defined(__NetBSD__)
1314     {
1315         switch (raw->TrackMode)
1316         {
1317         case YellowMode2:
1318             FIXME("YellowMode2: NIY\n");
1319             return ret;
1320         case XAForm2:
1321             FIXME("XAForm2: NIY\n");
1322             return ret;
1323         case CDDA:
1324             FIXME("CDDA: NIY\n");
1325             return ret;
1326         }
1327     }
1328 #endif
1329     *sz = sectSize * raw->SectorCount;
1330     ret = CDROM_GetStatusCode(io);
1331     return ret;
1332 }
1333
1334 /******************************************************************
1335  *              CDROM_ScsiPassThroughDirect
1336  *
1337  *
1338  */
1339 static DWORD CDROM_ScsiPassThroughDirect(int dev, PSCSI_PASS_THROUGH_DIRECT pPacket)
1340 {
1341     int ret = STATUS_NOT_SUPPORTED;
1342 #if defined(linux) && defined(CDROM_SEND_PACKET)
1343     struct linux_cdrom_generic_command cmd;
1344     struct request_sense sense;
1345     int io;
1346
1347     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1348         return STATUS_BUFFER_TOO_SMALL;
1349
1350     if (pPacket->CdbLength > 12)
1351         return STATUS_INVALID_PARAMETER;
1352
1353     if (pPacket->SenseInfoLength > sizeof(sense))
1354         return STATUS_INVALID_PARAMETER;
1355
1356     memset(&cmd, 0, sizeof(cmd));
1357     memset(&sense, 0, sizeof(sense));
1358
1359     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1360
1361     cmd.buffer         = pPacket->DataBuffer;
1362     cmd.buflen         = pPacket->DataTransferLength;
1363     cmd.sense          = &sense;
1364     cmd.quiet          = 0;
1365     cmd.timeout        = pPacket->TimeOutValue*HZ;
1366
1367     switch (pPacket->DataIn)
1368     {
1369     case SCSI_IOCTL_DATA_OUT:
1370         cmd.data_direction = CGC_DATA_WRITE;
1371         break;
1372     case SCSI_IOCTL_DATA_IN:
1373         cmd.data_direction = CGC_DATA_READ;
1374         break;
1375     case SCSI_IOCTL_DATA_UNSPECIFIED:
1376         cmd.data_direction = CGC_DATA_NONE;
1377         break;
1378     default:
1379        return STATUS_INVALID_PARAMETER;
1380     }
1381
1382     io = ioctl(cdrom_cache[dev].fd, CDROM_SEND_PACKET, &cmd);
1383
1384     if (pPacket->SenseInfoLength != 0)
1385     {
1386         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1387                &sense, pPacket->SenseInfoLength);
1388     }
1389
1390     pPacket->ScsiStatus = cmd.stat;
1391
1392     ret = CDROM_GetStatusCode(io);
1393 #endif
1394     return ret;
1395 }
1396
1397 /******************************************************************
1398  *              CDROM_ScsiPassThrough
1399  *
1400  *
1401  */
1402 static DWORD CDROM_ScsiPassThrough(int dev, PSCSI_PASS_THROUGH pPacket)
1403 {
1404     int ret = STATUS_NOT_SUPPORTED;
1405 #if defined(linux) && defined(CDROM_SEND_PACKET)
1406     struct linux_cdrom_generic_command cmd;
1407     struct request_sense sense;
1408     int io;
1409
1410     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1411         return STATUS_BUFFER_TOO_SMALL;
1412
1413     if (pPacket->CdbLength > 12)
1414         return STATUS_INVALID_PARAMETER;
1415
1416     if (pPacket->SenseInfoLength > sizeof(sense))
1417         return STATUS_INVALID_PARAMETER;
1418
1419     memset(&cmd, 0, sizeof(cmd));
1420     memset(&sense, 0, sizeof(sense));
1421
1422     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1423
1424     if ( pPacket->DataBufferOffset > 0x1000 )
1425     {
1426         cmd.buffer     = (void*)pPacket->DataBufferOffset;
1427     }
1428     else
1429     {
1430         cmd.buffer     = (char*)pPacket + pPacket->DataBufferOffset;
1431     }
1432     cmd.buflen         = pPacket->DataTransferLength;
1433     cmd.sense          = &sense;
1434     cmd.quiet          = 0;
1435     cmd.timeout        = pPacket->TimeOutValue*HZ;
1436
1437     switch (pPacket->DataIn)
1438     {
1439     case SCSI_IOCTL_DATA_OUT:
1440         cmd.data_direction = CGC_DATA_WRITE;
1441         break;
1442     case SCSI_IOCTL_DATA_IN:
1443         cmd.data_direction = CGC_DATA_READ;
1444         break;
1445     case SCSI_IOCTL_DATA_UNSPECIFIED:
1446         cmd.data_direction = CGC_DATA_NONE;
1447         break;
1448     default:
1449        return STATUS_INVALID_PARAMETER;
1450     }
1451
1452     io = ioctl(cdrom_cache[dev].fd, CDROM_SEND_PACKET, &cmd);
1453
1454     if (pPacket->SenseInfoLength != 0)
1455     {
1456         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1457                &sense, pPacket->SenseInfoLength);
1458     }
1459
1460     pPacket->ScsiStatus = cmd.stat;
1461
1462     ret = CDROM_GetStatusCode(io);
1463 #endif
1464     return ret;
1465 }
1466
1467 /******************************************************************
1468  *              CDROM_GetAddress
1469  *
1470  * implements IOCTL_SCSI_GET_ADDRESS
1471  */
1472 static DWORD CDROM_GetAddress(int dev, SCSI_ADDRESS* address)
1473 {
1474     int portnum, targetid;
1475
1476     address->Length = sizeof(SCSI_ADDRESS);
1477     address->PathId = 0; /* bus number */
1478     address->Lun = 0;
1479     if ( ! CDROM_GetIdeInterface(cdrom_cache[dev].fd, &portnum, &targetid))
1480         return STATUS_NOT_SUPPORTED;
1481
1482     address->PortNumber = portnum;
1483     address->TargetId = targetid;
1484     return STATUS_SUCCESS;
1485 }
1486
1487 /******************************************************************
1488  *              CDROM_DeviceIoControl
1489  *
1490  *
1491  */
1492 BOOL CDROM_DeviceIoControl(DWORD clientID, HANDLE hDevice, DWORD dwIoControlCode,
1493                            LPVOID lpInBuffer, DWORD nInBufferSize,
1494                            LPVOID lpOutBuffer, DWORD nOutBufferSize,
1495                            LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
1496 {
1497     DWORD       sz;
1498     DWORD       error = 0;
1499     int         dev;
1500
1501     TRACE("%lx[%c] %s %lx %ld %lx %ld %lx %lx\n",
1502           (DWORD)hDevice, 'A' + LOWORD(clientID), iocodex(dwIoControlCode), (DWORD)lpInBuffer, nInBufferSize,
1503           (DWORD)lpOutBuffer, nOutBufferSize, (DWORD)lpBytesReturned, (DWORD)lpOverlapped);
1504
1505     if (lpBytesReturned) *lpBytesReturned = 0;
1506     if (lpOverlapped)
1507     {
1508         FIXME("Overlapped isn't implemented yet\n");
1509         SetLastError(STATUS_NOT_SUPPORTED);
1510         return FALSE;
1511     }
1512
1513     SetLastError(0);
1514     if ((dev = CDROM_Open(hDevice, clientID)) == -1) return FALSE;
1515
1516     switch (dwIoControlCode)
1517     {
1518     case IOCTL_STORAGE_CHECK_VERIFY:
1519     case IOCTL_CDROM_CHECK_VERIFY:
1520         sz = 0;
1521         CDROM_ClearCacheEntry(dev);
1522         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1523             error = STATUS_INVALID_PARAMETER;
1524         else error = CDROM_Verify(dev);
1525         break;
1526
1527 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
1528
1529 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
1530 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
1531
1532     case IOCTL_STORAGE_LOAD_MEDIA:
1533     case IOCTL_CDROM_LOAD_MEDIA:
1534         sz = 0;
1535         CDROM_ClearCacheEntry(dev);
1536         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1537             error = STATUS_INVALID_PARAMETER;
1538         else error = CDROM_SetTray(dev, FALSE);
1539         break;
1540      case IOCTL_STORAGE_EJECT_MEDIA:
1541         sz = 0;
1542         CDROM_ClearCacheEntry(dev);
1543         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1544             error = STATUS_INVALID_PARAMETER;
1545         else error = CDROM_SetTray(dev, TRUE);
1546         break;
1547
1548     case IOCTL_CDROM_MEDIA_REMOVAL:
1549     case IOCTL_DISK_MEDIA_REMOVAL:
1550     case IOCTL_STORAGE_MEDIA_REMOVAL:
1551     case IOCTL_STORAGE_EJECTION_CONTROL:
1552         /* FIXME the last ioctl:s is not the same as the two others...
1553          * lockcount/owner should be handled */
1554         sz = 0;
1555         CDROM_ClearCacheEntry(dev);
1556         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1557         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) error = STATUS_BUFFER_TOO_SMALL;
1558         else error = CDROM_ControlEjection(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1559         break;
1560
1561 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
1562
1563     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1564         sz = sizeof(STORAGE_DEVICE_NUMBER);
1565         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1566         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1567         else error = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
1568         break;
1569
1570     case IOCTL_STORAGE_RESET_DEVICE:
1571         sz = 0;
1572         CDROM_ClearCacheEntry(dev);
1573         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1574             error = STATUS_INVALID_PARAMETER;
1575         else error = CDROM_ResetAudio(dev);
1576         break;
1577
1578     case IOCTL_CDROM_GET_CONTROL:
1579         sz = sizeof(CDROM_AUDIO_CONTROL);
1580         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1581         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1582         else error = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
1583         break;
1584
1585     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1586         sz = sizeof(DISK_GEOMETRY);
1587         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1588         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1589         else error = CDROM_GetDriveGeometry(dev, (DISK_GEOMETRY*)lpOutBuffer);
1590         break;
1591
1592     case IOCTL_CDROM_DISK_TYPE:
1593         sz = sizeof(CDROM_DISK_DATA);
1594         /* CDROM_ClearCacheEntry(dev); */
1595         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1596         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1597         else error = CDROM_GetDiskData(dev, (CDROM_DISK_DATA*)lpOutBuffer);
1598         break;
1599
1600 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
1601
1602     case IOCTL_CDROM_READ_Q_CHANNEL:
1603         sz = sizeof(SUB_Q_CHANNEL_DATA);
1604         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
1605             error = STATUS_INVALID_PARAMETER;
1606         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1607         else error = CDROM_ReadQChannel(dev, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1608                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1609         break;
1610
1611     case IOCTL_CDROM_READ_TOC:
1612         sz = sizeof(CDROM_TOC);
1613         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1614         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1615         else error = CDROM_ReadTOC(dev, (CDROM_TOC*)lpOutBuffer);
1616         break;
1617
1618 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
1619
1620     case IOCTL_CDROM_PAUSE_AUDIO:
1621         sz = 0;
1622         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1623             error = STATUS_INVALID_PARAMETER;
1624         else error = CDROM_PauseAudio(dev);
1625         break;
1626     case IOCTL_CDROM_PLAY_AUDIO_MSF:
1627         sz = 0;
1628         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1629         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1630         else error = CDROM_PlayAudioMSF(dev, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1631         break;
1632     case IOCTL_CDROM_RESUME_AUDIO:
1633         sz = 0;
1634         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1635             error = STATUS_INVALID_PARAMETER;
1636         else error = CDROM_ResumeAudio(dev);
1637         break;
1638     case IOCTL_CDROM_SEEK_AUDIO_MSF:
1639         sz = 0;
1640         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1641         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1642         else error = CDROM_SeekAudioMSF(dev, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1643         break;
1644     case IOCTL_CDROM_STOP_AUDIO:
1645         sz = 0;
1646         CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
1647         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1648             error = STATUS_INVALID_PARAMETER;
1649         else error = CDROM_StopAudio(dev);
1650         break;
1651     case IOCTL_CDROM_GET_VOLUME:
1652         sz = sizeof(VOLUME_CONTROL);
1653         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1654         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1655         else error = CDROM_GetVolume(dev, (VOLUME_CONTROL*)lpOutBuffer);
1656         break;
1657     case IOCTL_CDROM_SET_VOLUME:
1658         sz = 0;
1659         CDROM_ClearCacheEntry(dev);
1660         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1661             error = STATUS_INVALID_PARAMETER;
1662         else error = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
1663         break;
1664     case IOCTL_CDROM_RAW_READ:
1665         sz = 0;
1666         if (nInBufferSize < sizeof(RAW_READ_INFO)) error = STATUS_INVALID_PARAMETER;
1667         else if (lpOutBuffer == NULL) error = STATUS_BUFFER_TOO_SMALL;
1668         else error = CDROM_RawRead(dev, (const RAW_READ_INFO*)lpInBuffer,
1669                                    lpOutBuffer, nOutBufferSize, &sz);
1670         break;
1671     case IOCTL_SCSI_GET_ADDRESS:
1672         sz = sizeof(SCSI_ADDRESS);
1673         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1674         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1675         else error = CDROM_GetAddress(dev, (SCSI_ADDRESS*)lpOutBuffer);
1676         break;
1677     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
1678         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
1679         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1680         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) error = STATUS_BUFFER_TOO_SMALL;
1681         else error = CDROM_ScsiPassThroughDirect(dev, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1682         break;
1683     case IOCTL_SCSI_PASS_THROUGH:
1684         sz = sizeof(SCSI_PASS_THROUGH);
1685         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1686         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) error = STATUS_BUFFER_TOO_SMALL;
1687         else error = CDROM_ScsiPassThrough(dev, (PSCSI_PASS_THROUGH)lpOutBuffer);
1688         break;
1689
1690     default:
1691         FIXME("Unsupported IOCTL %lx\n", dwIoControlCode);
1692         sz = 0;
1693         error = STATUS_INVALID_PARAMETER;
1694         break;
1695     }
1696
1697     if (lpBytesReturned) *lpBytesReturned = sz;
1698     if (error)
1699     {
1700         SetLastError(error);
1701         return FALSE;
1702     }
1703     CDROM_Close(clientID);
1704     return TRUE;
1705 }