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