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