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