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