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