Implemented WNetEnumCachedPasswords.
[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 static int CDROM_GetInterfaceInfo(int fd, int* port, int* iface, int* device,int* lun)
382 {
383 #if defined(linux)
384     struct stat st;
385     if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode))
386     {
387         FIXME("cdrom not a block device!!!\n");
388         return 0;
389     }
390     *port = 0;
391     *iface = 0;
392     *device = 0;
393     *lun = 0;
394     switch (major(st.st_rdev)) {
395     case IDE0_MAJOR: *iface = 0; break;
396     case IDE1_MAJOR: *iface = 1; break;
397     case IDE2_MAJOR: *iface = 2; break;
398     case IDE3_MAJOR: *iface = 3; break;
399     case IDE4_MAJOR: *iface = 4; break;
400     case IDE5_MAJOR: *iface = 5; break;
401     case IDE6_MAJOR: *iface = 6; break;
402     case IDE7_MAJOR: *iface = 7; break;
403     default: *port = 1; break;
404     }
405
406     if (*port == 0)
407         *device = (minor(st.st_rdev) >> 6);
408     else
409     {
410 #ifdef SCSI_IOCTL_GET_IDLUN
411         UINT32 idlun[2];
412         if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun) != -1)
413         {
414             *port = ((idlun[0] >> 24) & 0xff) + 1;
415             *iface = (idlun[0] >> 16) & 0xff;
416             *device = idlun[0] & 0xff;
417             *lun = (idlun[0] >> 8) & 0xff;
418         }
419         else
420 #endif
421         {
422             WARN("CD-ROM device (%d, %d) not supported\n", major(st.st_rdev), minor(st.st_rdev));
423             return 0;
424         }
425     }
426     return 1;
427 #elif defined(__NetBSD__)
428     struct scsi_addr addr;
429     if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1)
430     {
431         switch (addr.type) 
432         {
433         case TYPE_SCSI:  *port = 1;
434             *iface = addr.addr.scsi.scbus;
435             *device = addr.addr.scsi.target;
436             *lun = addr.addr.scsi.lun;
437             break;
438         case TYPE_ATAPI: *port = 0;
439             *iface = addr.addr.atapi.atbus;
440             *device = addr.addr.atapi.drive;
441             *lun = 0;
442             break;
443         }
444         return 1;
445     }
446     return 0;
447 #elif defined(__FreeBSD__)
448     FIXME("not implemented for BSD\n");
449     return 0;
450 #else
451     FIXME("not implemented for nonlinux\n");
452     return 0;
453 #endif
454 }
455
456
457 /******************************************************************
458  *              CDROM_Open
459  *
460  */
461 static NTSTATUS CDROM_Open(int fd, int* dev)
462 {
463     struct stat st;
464     NTSTATUS ret = STATUS_SUCCESS;
465     int         empty = -1;
466
467     fstat(fd, &st);
468
469     RtlEnterCriticalSection( &cache_section );
470     for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++)
471     {
472         if (empty == -1 &&
473             cdrom_cache[*dev].device == 0 &&
474             cdrom_cache[*dev].inode == 0)
475             empty = *dev;
476         else if (cdrom_cache[*dev].device == st.st_dev &&
477                  cdrom_cache[*dev].inode == st.st_ino)
478             break;
479     }
480     if (*dev == MAX_CACHE_ENTRIES)
481     {
482         if (empty == -1) ret = STATUS_NOT_IMPLEMENTED;
483         else
484         {
485             *dev = empty;
486             cdrom_cache[*dev].device  = st.st_dev;
487             cdrom_cache[*dev].inode   = st.st_ino;
488         }
489     }
490     RtlLeaveCriticalSection( &cache_section );
491
492     TRACE("%d, %d\n", *dev, fd);
493     return ret;
494 }
495
496 /******************************************************************
497  *              CDROM_GetStatusCode
498  *
499  *
500  */
501 static NTSTATUS CDROM_GetStatusCode(int io)
502 {
503     if (io == 0) return STATUS_SUCCESS;
504     switch (errno)
505     {
506     case EIO:
507 #ifdef ENOMEDIUM
508     case ENOMEDIUM:
509 #endif
510         return STATUS_NO_MEDIA_IN_DEVICE;
511     case EPERM:
512         return STATUS_ACCESS_DENIED;
513     case EINVAL:
514         return STATUS_INVALID_PARAMETER;
515     /* case EBADF: Bad file descriptor */
516     case EOPNOTSUPP:
517         return STATUS_NOT_SUPPORTED;
518     }
519     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
520     return STATUS_IO_DEVICE_ERROR;
521 }
522
523 /******************************************************************
524  *              CDROM_GetControl
525  *
526  */
527 static NTSTATUS CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
528 {
529     cac->LbaFormat = 0; /* FIXME */
530     cac->LogicalBlocksPerSecond = 1; /* FIXME */
531     return  STATUS_NOT_SUPPORTED;
532 }
533
534 /******************************************************************
535  *              CDROM_GetDeviceNumber
536  *
537  */
538 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
539 {
540     return STATUS_NOT_SUPPORTED;
541 }
542
543 /******************************************************************
544  *              CDROM_GetDriveGeometry
545  *
546  */
547 static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg)
548 {
549   CDROM_TOC     toc;
550   NTSTATUS      ret = 0;
551   int           fsize = 0;
552
553   if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
554
555   fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
556         - FRAME_OF_TOC(toc, 1); /* Total size in frames */
557   
558   dg->Cylinders.u.LowPart = fsize / (64 * 32); 
559   dg->Cylinders.u.HighPart = 0; 
560   dg->MediaType = RemovableMedia;  
561   dg->TracksPerCylinder = 64; 
562   dg->SectorsPerTrack = 32;  
563   dg->BytesPerSector= 2048; 
564   return ret;
565 }
566
567 /**************************************************************************
568  *                              CDROM_Reset                     [internal]
569  */
570 static NTSTATUS CDROM_ResetAudio(int fd)
571 {
572 #if defined(linux)
573     return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
574 #elif defined(__FreeBSD__) || defined(__NetBSD__)
575     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
576 #else
577     return STATUS_NOT_SUPPORTED;
578 #endif
579 }
580
581 /******************************************************************
582  *              CDROM_SetTray
583  *
584  *
585  */
586 static NTSTATUS CDROM_SetTray(int fd, BOOL doEject)
587 {
588 #if defined(linux)
589     return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
590 #elif defined(__FreeBSD__) || defined(__NetBSD__)
591     return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
592                                (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
593                                (ioctl(fd, CDIOCPREVENT, NULL)));
594 #else
595     return STATUS_NOT_SUPPORTED;
596 #endif
597 }
598
599 /******************************************************************
600  *              CDROM_ControlEjection
601  *
602  *
603  */
604 static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv)
605 {
606 #if defined(linux)
607     return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
608 #elif defined(__FreeBSD__) || defined(__NetBSD__)
609     return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
610 #else
611     return STATUS_NOT_SUPPORTED;
612 #endif
613 }
614
615 /******************************************************************
616  *              CDROM_ReadTOC
617  *
618  *
619  */
620 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
621 {
622     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
623
624     if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
625         return STATUS_INVALID_PARAMETER;
626
627     RtlEnterCriticalSection( &cache_section );
628     if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd)))
629     {
630         *toc = cdrom_cache[dev].toc;
631         ret = STATUS_SUCCESS;
632     }
633     RtlLeaveCriticalSection( &cache_section );
634     return ret;
635 }
636
637 /******************************************************************
638  *              CDROM_GetDiskData
639  *
640  *
641  */
642 static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data)
643 {
644     CDROM_TOC   toc;
645     NTSTATUS    ret;
646     int         i;
647
648     if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
649     data->DiskData = 0;
650     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
651         if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
652             data->DiskData |= CDROM_DISK_DATA_TRACK;
653         else
654             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
655     }
656     return STATUS_SUCCESS;
657 }
658
659 /******************************************************************
660  *              CDROM_ReadQChannel
661  *
662  *
663  */
664 static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt,
665                                    SUB_Q_CHANNEL_DATA* data)
666 {
667     NTSTATUS            ret = STATUS_NOT_SUPPORTED;
668 #ifdef linux
669     unsigned            size;
670     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
671     int                 io;
672     struct cdrom_subchnl        sc;
673     sc.cdsc_format = CDROM_MSF;
674
675     io = ioctl(fd, CDROMSUBCHNL, &sc);
676     if (io == -1)
677     {
678         TRACE("opened or no_media (%s)!\n", strerror(errno));
679         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
680         CDROM_ClearCacheEntry(dev);
681         goto end;
682     }
683
684     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
685
686     switch (sc.cdsc_audiostatus) {
687     case CDROM_AUDIO_INVALID:
688         CDROM_ClearCacheEntry(dev);
689         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
690         break;
691     case CDROM_AUDIO_NO_STATUS:
692         CDROM_ClearCacheEntry(dev);
693         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
694         break;
695     case CDROM_AUDIO_PLAY:
696         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
697         break;
698     case CDROM_AUDIO_PAUSED:
699         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
700         break;
701     case CDROM_AUDIO_COMPLETED:
702         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
703         break;
704     case CDROM_AUDIO_ERROR:
705         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
706         break;
707     default:
708         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
709         break;
710     }
711     switch (fmt->Format)
712     {
713     case IOCTL_CDROM_CURRENT_POSITION:
714         size = sizeof(SUB_Q_CURRENT_POSITION);
715         RtlEnterCriticalSection( &cache_section );
716         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
717           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
718           data->CurrentPosition.Control = sc.cdsc_ctrl; 
719           data->CurrentPosition.ADR = sc.cdsc_adr; 
720           data->CurrentPosition.TrackNumber = sc.cdsc_trk; 
721           data->CurrentPosition.IndexNumber = sc.cdsc_ind; 
722
723           data->CurrentPosition.AbsoluteAddress[0] = 0; 
724           data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; 
725           data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
726           data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
727  
728           data->CurrentPosition.TrackRelativeAddress[0] = 0; 
729           data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; 
730           data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
731           data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
732
733           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
734         }
735         else /* not playing */
736         {
737           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
738           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
739         }
740         RtlLeaveCriticalSection( &cache_section );
741         break;
742     case IOCTL_CDROM_MEDIA_CATALOG:
743         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
744         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
745         {
746             struct cdrom_mcn mcn;
747             if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
748
749             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
750             data->MediaCatalog.Mcval = 0; /* FIXME */
751             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
752             data->MediaCatalog.MediaCatalog[14] = 0;
753         }
754         break;
755     case IOCTL_CDROM_TRACK_ISRC:
756         size = sizeof(SUB_Q_CURRENT_POSITION);
757         FIXME("TrackIsrc: NIY on linux\n");
758         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
759         data->TrackIsrc.Tcval = 0;
760         io = 0;
761         break;
762     }
763
764  end:
765     ret = CDROM_GetStatusCode(io);
766 #elif defined(__FreeBSD__) || defined(__NetBSD__)
767     unsigned            size;
768     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
769     int                 io;
770     struct ioc_read_subchannel  read_sc;
771     struct cd_sub_channel_info  sc;
772
773     read_sc.address_format = CD_MSF_FORMAT;
774     read_sc.track          = 0;
775     read_sc.data_len       = sizeof(sc);
776     read_sc.data           = &sc;
777     switch (fmt->Format)
778     {
779     case IOCTL_CDROM_CURRENT_POSITION:
780         read_sc.data_format    = CD_CURRENT_POSITION;
781         break;
782     case IOCTL_CDROM_MEDIA_CATALOG:
783         read_sc.data_format    = CD_MEDIA_CATALOG;
784         break;
785     case IOCTL_CDROM_TRACK_ISRC:
786         read_sc.data_format    = CD_TRACK_INFO;
787         sc.what.track_info.track_number = data->TrackIsrc.Track;
788         break;
789     }
790     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
791     if (io == -1)
792     {
793         TRACE("opened or no_media (%s)!\n", strerror(errno));
794         CDROM_ClearCacheEntry(dev);
795         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
796         goto end;
797     }
798
799     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
800
801     switch (sc.header.audio_status) {
802     case CD_AS_AUDIO_INVALID:
803         CDROM_ClearCacheEntry(dev);
804         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
805         break;
806     case CD_AS_NO_STATUS:
807         CDROM_ClearCacheEntry(dev);
808         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
809         break;
810     case CD_AS_PLAY_IN_PROGRESS:
811         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
812         break;
813     case CD_AS_PLAY_PAUSED:
814         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
815         break;
816     case CD_AS_PLAY_COMPLETED:
817         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
818         break;
819     case CD_AS_PLAY_ERROR:
820         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
821         break;
822     default:
823         TRACE("status=%02X !\n", sc.header.audio_status);
824     }
825     switch (fmt->Format)
826     {
827     case IOCTL_CDROM_CURRENT_POSITION:
828         size = sizeof(SUB_Q_CURRENT_POSITION);
829         RtlEnterCriticalSection( &cache_section );
830         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
831           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
832           data->CurrentPosition.Control = sc.what.position.control;
833           data->CurrentPosition.ADR = sc.what.position.addr_type;
834           data->CurrentPosition.TrackNumber = sc.what.position.track_number;
835           data->CurrentPosition.IndexNumber = sc.what.position.index_number;
836
837           data->CurrentPosition.AbsoluteAddress[0] = 0;
838           data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
839           data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
840           data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
841           data->CurrentPosition.TrackRelativeAddress[0] = 0;
842           data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
843           data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
844           data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
845           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
846         }
847         else { /* not playing */
848           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
849           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
850         }
851         RtlLeaveCriticalSection( &cache_section );
852         break;
853     case IOCTL_CDROM_MEDIA_CATALOG:
854         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
855         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
856         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
857         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
858         break;
859     case IOCTL_CDROM_TRACK_ISRC:
860         size = sizeof(SUB_Q_CURRENT_POSITION);
861         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
862         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
863         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
864         break;
865     }
866
867  end:
868     ret = CDROM_GetStatusCode(io);
869 #endif
870     return ret;
871 }
872
873 /******************************************************************
874  *              CDROM_Verify
875  *
876  *
877  */
878 static NTSTATUS CDROM_Verify(int dev, int fd)
879 {
880     /* quick implementation */
881     CDROM_SUB_Q_DATA_FORMAT     fmt;
882     SUB_Q_CHANNEL_DATA          data;
883
884     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
885     return CDROM_ReadQChannel(dev, fd, &fmt, &data) ? 1 : 0;
886 }
887
888 /******************************************************************
889  *              CDROM_PlayAudioMSF
890  *
891  *
892  */
893 static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf)
894 {
895     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
896 #ifdef linux
897     struct      cdrom_msf       msf;
898     int         io;
899
900     msf.cdmsf_min0   = audio_msf->StartingM;
901     msf.cdmsf_sec0   = audio_msf->StartingS;
902     msf.cdmsf_frame0 = audio_msf->StartingF;
903     msf.cdmsf_min1   = audio_msf->EndingM;
904     msf.cdmsf_sec1   = audio_msf->EndingS;
905     msf.cdmsf_frame1 = audio_msf->EndingF;
906
907     io = ioctl(fd, CDROMSTART);
908     if (io == -1)
909     {
910         WARN("motor doesn't start !\n");
911         goto end;
912     }
913     io = ioctl(fd, CDROMPLAYMSF, &msf);
914     if (io == -1)
915     {
916         WARN("device doesn't play !\n");
917         goto end;
918     }
919     TRACE("msf = %d:%d:%d %d:%d:%d\n",
920           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
921           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
922  end:
923     ret = CDROM_GetStatusCode(io);
924 #elif defined(__FreeBSD__) || defined(__NetBSD__)
925     struct      ioc_play_msf    msf;
926     int         io;
927
928     msf.start_m      = audio_msf->StartingM;
929     msf.start_s      = audio_msf->StartingS;
930     msf.start_f      = audio_msf->StartingF;
931     msf.end_m        = audio_msf->EndingM;
932     msf.end_s        = audio_msf->EndingS;
933     msf.end_f        = audio_msf->EndingF;
934
935     io = ioctl(fd, CDIOCSTART, NULL);
936     if (io == -1)
937     {
938         WARN("motor doesn't start !\n");
939         goto end;
940     }
941     io = ioctl(fd, CDIOCPLAYMSF, &msf);
942     if (io == -1)
943     {
944         WARN("device doesn't play !\n");
945         goto end;
946     }
947     TRACE("msf = %d:%d:%d %d:%d:%d\n",
948           msf.start_m, msf.start_s, msf.start_f,
949           msf.end_m,   msf.end_s,   msf.end_f);
950 end:
951     ret = CDROM_GetStatusCode(io);
952 #endif
953     return ret;
954 }
955
956 /******************************************************************
957  *              CDROM_SeekAudioMSF
958  *
959  *
960  */
961 static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf)
962 {
963     CDROM_TOC toc;
964     int i, io, frame;
965     SUB_Q_CURRENT_POSITION *cp;
966 #if defined(linux)
967     struct cdrom_msf0   msf;
968     struct cdrom_subchnl sc;
969 #elif defined(__FreeBSD__) || defined(__NetBSD__)
970     struct ioc_play_msf msf;
971     struct ioc_read_subchannel  read_sc;
972     struct cd_sub_channel_info  sc;
973     int final_frame;
974 #endif
975
976     /* Use the information on the TOC to compute the new current
977      * position, which is shadowed on the cache. [Portable]. */
978     frame = FRAME_OF_MSF(*audio_msf);
979
980     if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io;
981
982     for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
983       if (FRAME_OF_TOC(toc,i)>frame) break;
984     if (i <= toc.FirstTrack || i > toc.LastTrack+1)
985       return STATUS_INVALID_PARAMETER;
986     i--;
987     RtlEnterCriticalSection( &cache_section );
988     cp = &cdrom_cache[dev].CurrentPosition;
989     cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION; 
990     cp->Control = toc.TrackData[i-toc.FirstTrack].Control; 
991     cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; 
992     cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
993     cp->IndexNumber = 0; /* FIXME: where do they keep these? */
994     cp->AbsoluteAddress[0] = 0; 
995     cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
996     cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
997     cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
998     frame -= FRAME_OF_TOC(toc,i);
999     cp->TrackRelativeAddress[0] = 0;
1000     MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
1001     RtlLeaveCriticalSection( &cache_section );
1002
1003     /* If playing, then issue a seek command, otherwise do nothing */
1004 #ifdef linux
1005     sc.cdsc_format = CDROM_MSF;
1006
1007     io = ioctl(fd, CDROMSUBCHNL, &sc);
1008     if (io == -1)
1009     {
1010         TRACE("opened or no_media (%s)!\n", strerror(errno));
1011         CDROM_ClearCacheEntry(dev);
1012         return CDROM_GetStatusCode(io);
1013     }
1014     if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1015     {
1016       msf.minute = audio_msf->M;
1017       msf.second = audio_msf->S;
1018       msf.frame  = audio_msf->F;
1019       return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf));
1020     }
1021     return STATUS_SUCCESS;
1022 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1023     read_sc.address_format = CD_MSF_FORMAT;
1024     read_sc.track          = 0;
1025     read_sc.data_len       = sizeof(sc);
1026     read_sc.data           = &sc;
1027     read_sc.data_format    = CD_CURRENT_POSITION;
1028
1029     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1030     if (io == -1)
1031     {
1032         TRACE("opened or no_media (%s)!\n", strerror(errno));
1033         CDROM_ClearCacheEntry(dev);
1034         return CDROM_GetStatusCode(io);
1035     }
1036     if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) 
1037     {
1038
1039       msf.start_m      = audio_msf->M;
1040       msf.start_s      = audio_msf->S;
1041       msf.start_f      = audio_msf->F;
1042       final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1043       MSF_OF_FRAME(msf.end_m, final_frame);
1044
1045       return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
1046     }
1047     return STATUS_SUCCESS;
1048 #else
1049     return STATUS_NOT_SUPPORTED;
1050 #endif
1051 }
1052
1053 /******************************************************************
1054  *              CDROM_PauseAudio
1055  *
1056  *
1057  */
1058 static NTSTATUS CDROM_PauseAudio(int fd)
1059 {
1060 #if defined(linux)
1061     return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
1062 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1063     return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
1064 #else
1065     return STATUS_NOT_SUPPORTED;
1066 #endif
1067 }
1068
1069 /******************************************************************
1070  *              CDROM_ResumeAudio
1071  *
1072  *
1073  */
1074 static NTSTATUS CDROM_ResumeAudio(int fd)
1075 {
1076 #if defined(linux)
1077     return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
1078 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1079     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
1080 #else
1081     return STATUS_NOT_SUPPORTED;
1082 #endif
1083 }
1084
1085 /******************************************************************
1086  *              CDROM_StopAudio
1087  *
1088  *
1089  */
1090 static NTSTATUS CDROM_StopAudio(int fd)
1091 {
1092 #if defined(linux)
1093     return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
1094 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1095     return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
1096 #else
1097     return STATUS_NOT_SUPPORTED;
1098 #endif
1099 }
1100
1101 /******************************************************************
1102  *              CDROM_GetVolume
1103  *
1104  *
1105  */
1106 static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc)
1107 {
1108 #if defined(linux)
1109     struct cdrom_volctrl volc;
1110     int io;
1111
1112     io = ioctl(fd, CDROMVOLREAD, &volc);
1113     if (io != -1)
1114     {
1115         vc->PortVolume[0] = volc.channel0;
1116         vc->PortVolume[1] = volc.channel1;
1117         vc->PortVolume[2] = volc.channel2;
1118         vc->PortVolume[3] = volc.channel3;
1119     }
1120     return CDROM_GetStatusCode(io);
1121 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1122     struct  ioc_vol     volc;
1123     int io;
1124
1125     io = ioctl(fd, CDIOCGETVOL, &volc);
1126     if (io != -1)
1127     {
1128         vc->PortVolume[0] = volc.vol[0];
1129         vc->PortVolume[1] = volc.vol[1];
1130         vc->PortVolume[2] = volc.vol[2];
1131         vc->PortVolume[3] = volc.vol[3];
1132     }
1133     return CDROM_GetStatusCode(io);
1134 #else
1135     return STATUS_NOT_SUPPORTED;
1136 #endif
1137 }
1138
1139 /******************************************************************
1140  *              CDROM_SetVolume
1141  *
1142  *
1143  */
1144 static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
1145 {
1146 #if defined(linux)
1147     struct cdrom_volctrl volc;
1148
1149     volc.channel0 = vc->PortVolume[0];
1150     volc.channel1 = vc->PortVolume[1];
1151     volc.channel2 = vc->PortVolume[2];
1152     volc.channel3 = vc->PortVolume[3];
1153
1154     return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc));
1155 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1156     struct  ioc_vol     volc;
1157
1158     volc.vol[0] = vc->PortVolume[0];
1159     volc.vol[1] = vc->PortVolume[1];
1160     volc.vol[2] = vc->PortVolume[2];
1161     volc.vol[3] = vc->PortVolume[3];
1162
1163     return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
1164 #else
1165     return STATUS_NOT_SUPPORTED;
1166 #endif
1167 }
1168
1169 /******************************************************************
1170  *              CDROM_RawRead
1171  *
1172  *
1173  */
1174 static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1175 {
1176     int         ret = STATUS_NOT_SUPPORTED;
1177     int         io = -1;
1178     DWORD       sectSize;
1179
1180     switch (raw->TrackMode)
1181     {
1182     case YellowMode2:   sectSize = 2336;        break;
1183     case XAForm2:       sectSize = 2328;        break;
1184     case CDDA:          sectSize = 2352;        break;
1185     default:    return STATUS_INVALID_PARAMETER;
1186     }
1187     if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
1188     /* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
1189      * even if a larger size if read...
1190      */
1191 #if defined(linux)
1192     {
1193         struct cdrom_read       cdr;
1194         struct cdrom_read_audio cdra;
1195
1196         switch (raw->TrackMode)
1197         {
1198         case YellowMode2:
1199             if (raw->DiskOffset.u.HighPart) FIXME("Unsupported value\n");
1200             cdr.cdread_lba = raw->DiskOffset.u.LowPart; /* FIXME ? */
1201             cdr.cdread_bufaddr = buffer;
1202             cdr.cdread_buflen = raw->SectorCount * sectSize;
1203             io = ioctl(fd, CDROMREADMODE2, &cdr);
1204             break;
1205         case XAForm2:
1206             FIXME("XAForm2: NIY\n");
1207             return ret;
1208         case CDDA:
1209             /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
1210              * between by NT2K box and this... should check on the same drive...
1211              * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
1212              * (linux/NT).
1213              * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
1214              * talking of 0.2 ms of sound
1215              */
1216             /* 2048 = 2 ** 11 */
1217             if (raw->DiskOffset.u.HighPart & ~2047) FIXME("Unsupported value\n");
1218             cdra.addr.lba = ((raw->DiskOffset.u.LowPart >> 11) |
1219                 (raw->DiskOffset.u.HighPart << (32 - 11))) - 1;
1220             FIXME("reading at %u\n", cdra.addr.lba);
1221             cdra.addr_format = CDROM_LBA;
1222             cdra.nframes = raw->SectorCount;
1223             cdra.buf = buffer;
1224             io = ioctl(fd, CDROMREADAUDIO, &cdra);
1225             break;
1226         default:
1227             FIXME("NIY: %d\n", raw->TrackMode);
1228             return ret;
1229         }
1230     }
1231 #else
1232     {
1233         switch (raw->TrackMode)
1234         {
1235         case YellowMode2:
1236             FIXME("YellowMode2: NIY\n");
1237             return ret;
1238         case XAForm2:
1239             FIXME("XAForm2: NIY\n");
1240             return ret;
1241         case CDDA:
1242             FIXME("CDDA: NIY\n");
1243             return ret;
1244         }
1245     }
1246 #endif
1247
1248     *sz = sectSize * raw->SectorCount;
1249     ret = CDROM_GetStatusCode(io);
1250     return ret;
1251 }
1252
1253 /******************************************************************
1254  *              CDROM_ScsiPassThroughDirect
1255  *
1256  *
1257  */
1258 static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket)
1259 {
1260     int ret = STATUS_NOT_SUPPORTED;
1261 #if defined(linux) && defined(CDROM_SEND_PACKET)
1262     struct linux_cdrom_generic_command cmd;
1263     struct request_sense sense;
1264     int io;
1265
1266     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1267         return STATUS_BUFFER_TOO_SMALL;
1268
1269     if (pPacket->CdbLength > 12)
1270         return STATUS_INVALID_PARAMETER;
1271
1272     if (pPacket->SenseInfoLength > sizeof(sense))
1273         return STATUS_INVALID_PARAMETER;
1274
1275     memset(&cmd, 0, sizeof(cmd));
1276     memset(&sense, 0, sizeof(sense));
1277
1278     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1279
1280     cmd.buffer         = pPacket->DataBuffer;
1281     cmd.buflen         = pPacket->DataTransferLength;
1282     cmd.sense          = &sense;
1283     cmd.quiet          = 0;
1284     cmd.timeout        = pPacket->TimeOutValue*HZ;
1285
1286     switch (pPacket->DataIn)
1287     {
1288     case SCSI_IOCTL_DATA_OUT:
1289         cmd.data_direction = CGC_DATA_WRITE;
1290         break;
1291     case SCSI_IOCTL_DATA_IN:
1292         cmd.data_direction = CGC_DATA_READ;
1293         break;
1294     case SCSI_IOCTL_DATA_UNSPECIFIED:
1295         cmd.data_direction = CGC_DATA_NONE;
1296         break;
1297     default:
1298        return STATUS_INVALID_PARAMETER;
1299     }
1300
1301     io = ioctl(fd, CDROM_SEND_PACKET, &cmd);
1302
1303     if (pPacket->SenseInfoLength != 0)
1304     {
1305         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1306                &sense, pPacket->SenseInfoLength);
1307     }
1308
1309     pPacket->ScsiStatus = cmd.stat;
1310
1311     ret = CDROM_GetStatusCode(io);
1312
1313 #elif defined(__NetBSD__)
1314     scsireq_t cmd;
1315     int io;
1316
1317     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1318         return STATUS_BUFFER_TOO_SMALL;
1319
1320     if (pPacket->CdbLength > 12)
1321         return STATUS_INVALID_PARAMETER;
1322
1323     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1324         return STATUS_INVALID_PARAMETER;
1325
1326     memset(&cmd, 0, sizeof(cmd));
1327     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1328
1329     cmd.cmdlen         = pPacket->CdbLength;
1330     cmd.databuf        = pPacket->DataBuffer;
1331     cmd.datalen        = pPacket->DataTransferLength;
1332     cmd.senselen       = pPacket->SenseInfoLength;
1333     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1334
1335     switch (pPacket->DataIn)
1336     {
1337     case SCSI_IOCTL_DATA_OUT:
1338         cmd.flags |= SCCMD_WRITE;
1339         break;
1340     case SCSI_IOCTL_DATA_IN:
1341         cmd.flags |= SCCMD_READ;
1342         break;
1343     case SCSI_IOCTL_DATA_UNSPECIFIED:
1344         cmd.flags = 0;
1345         break;
1346     default:
1347        return STATUS_INVALID_PARAMETER;
1348     }
1349
1350     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1351
1352     switch (cmd.retsts)
1353     {
1354     case SCCMD_OK:         break;
1355     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1356                            break;
1357     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1358                            break;
1359     case SCCMD_SENSE:      break;
1360     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1361                            break;
1362     }
1363
1364     if (pPacket->SenseInfoLength != 0)
1365     {
1366         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1367                cmd.sense, pPacket->SenseInfoLength);
1368     }
1369
1370     pPacket->ScsiStatus = cmd.status;
1371
1372     ret = CDROM_GetStatusCode(io);
1373 #endif
1374     return ret;
1375 }
1376
1377 /******************************************************************
1378  *              CDROM_ScsiPassThrough
1379  *
1380  *
1381  */
1382 static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket)
1383 {
1384     int ret = STATUS_NOT_SUPPORTED;
1385 #if defined(linux) && defined(CDROM_SEND_PACKET)
1386     struct linux_cdrom_generic_command cmd;
1387     struct request_sense sense;
1388     int io;
1389
1390     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1391         return STATUS_BUFFER_TOO_SMALL;
1392
1393     if (pPacket->CdbLength > 12)
1394         return STATUS_INVALID_PARAMETER;
1395
1396     if (pPacket->SenseInfoLength > sizeof(sense))
1397         return STATUS_INVALID_PARAMETER;
1398
1399     memset(&cmd, 0, sizeof(cmd));
1400     memset(&sense, 0, sizeof(sense));
1401
1402     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1403
1404     if ( pPacket->DataBufferOffset > 0x1000 )
1405     {
1406         cmd.buffer     = (void*)pPacket->DataBufferOffset;
1407     }
1408     else
1409     {
1410         cmd.buffer     = (char*)pPacket + pPacket->DataBufferOffset;
1411     }
1412     cmd.buflen         = pPacket->DataTransferLength;
1413     cmd.sense          = &sense;
1414     cmd.quiet          = 0;
1415     cmd.timeout        = pPacket->TimeOutValue*HZ;
1416
1417     switch (pPacket->DataIn)
1418     {
1419     case SCSI_IOCTL_DATA_OUT:
1420         cmd.data_direction = CGC_DATA_WRITE;
1421         break;
1422     case SCSI_IOCTL_DATA_IN:
1423         cmd.data_direction = CGC_DATA_READ;
1424         break;
1425     case SCSI_IOCTL_DATA_UNSPECIFIED:
1426         cmd.data_direction = CGC_DATA_NONE;
1427         break;
1428     default:
1429        return STATUS_INVALID_PARAMETER;
1430     }
1431
1432     io = ioctl(fd, CDROM_SEND_PACKET, &cmd);
1433
1434     if (pPacket->SenseInfoLength != 0)
1435     {
1436         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1437                &sense, pPacket->SenseInfoLength);
1438     }
1439
1440     pPacket->ScsiStatus = cmd.stat;
1441
1442     ret = CDROM_GetStatusCode(io);
1443
1444 #elif defined(__NetBSD__)
1445     scsireq_t cmd;
1446     int io;
1447
1448     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1449         return STATUS_BUFFER_TOO_SMALL;
1450
1451     if (pPacket->CdbLength > 12)
1452         return STATUS_INVALID_PARAMETER;
1453
1454     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1455         return STATUS_INVALID_PARAMETER;
1456
1457     memset(&cmd, 0, sizeof(cmd));
1458     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1459
1460     if ( pPacket->DataBufferOffset > 0x1000 )
1461     {
1462         cmd.databuf     = (void*)pPacket->DataBufferOffset;
1463     }
1464     else
1465     {
1466         cmd.databuf     = (char*)pPacket + pPacket->DataBufferOffset;
1467     }
1468
1469     cmd.cmdlen         = pPacket->CdbLength;
1470     cmd.datalen        = pPacket->DataTransferLength;
1471     cmd.senselen       = pPacket->SenseInfoLength;
1472     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1473
1474     switch (pPacket->DataIn)
1475     {
1476     case SCSI_IOCTL_DATA_OUT:
1477         cmd.flags |= SCCMD_WRITE;
1478         break;
1479     case SCSI_IOCTL_DATA_IN:
1480         cmd.flags |= SCCMD_READ;
1481         break;
1482     case SCSI_IOCTL_DATA_UNSPECIFIED:
1483         cmd.flags = 0;
1484         break;
1485     default:
1486        return STATUS_INVALID_PARAMETER;
1487     }
1488
1489     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1490
1491     switch (cmd.retsts)
1492     {
1493     case SCCMD_OK:         break;
1494     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1495                            break;
1496     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1497                            break;
1498     case SCCMD_SENSE:      break;
1499     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1500                            break;
1501     }
1502
1503     if (pPacket->SenseInfoLength != 0)
1504     {
1505         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1506                cmd.sense, pPacket->SenseInfoLength);
1507     }
1508
1509     pPacket->ScsiStatus = cmd.status;
1510
1511     ret = CDROM_GetStatusCode(io);
1512 #endif
1513     return ret;
1514 }
1515
1516 /******************************************************************
1517  *              CDROM_ScsiGetCaps
1518  *
1519  *
1520  */
1521 static NTSTATUS CDROM_ScsiGetCaps(PIO_SCSI_CAPABILITIES caps)
1522 {
1523     NTSTATUS    ret = STATUS_NOT_IMPLEMENTED;
1524
1525     caps->Length = sizeof(*caps);
1526 #if defined(linux)
1527     caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1528     caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1529     caps->SupportedAsynchronousEvents = TRUE;
1530     caps->AlignmentMask = getpagesize();
1531     caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1532     caps->AdapterScansDown = FALSE; /* FIXME ? */
1533     caps->AdapterUsesPio = FALSE; /* FIXME ? */
1534     ret = STATUS_SUCCESS;
1535 #else
1536     FIXME("Unimplemented\n");
1537 #endif
1538     return ret;
1539 }
1540
1541 /******************************************************************
1542  *              CDROM_GetAddress
1543  *
1544  * implements IOCTL_SCSI_GET_ADDRESS
1545  */
1546 static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address)
1547 {
1548     int portnum, busid, targetid, lun;
1549
1550     address->Length = sizeof(SCSI_ADDRESS);
1551     if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
1552         return STATUS_NOT_SUPPORTED;
1553
1554     address->PortNumber = portnum;
1555     address->PathId = busid; /* bus number */
1556     address->TargetId = targetid;
1557     address->Lun = lun;
1558     return STATUS_SUCCESS;
1559 }
1560
1561 /******************************************************************
1562  *              CDROM_DeviceIoControl
1563  *
1564  *
1565  */
1566 NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, 
1567                                HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1568                                PVOID UserApcContext, 
1569                                PIO_STATUS_BLOCK piosb, 
1570                                ULONG dwIoControlCode,
1571                                LPVOID lpInBuffer, DWORD nInBufferSize,
1572                                LPVOID lpOutBuffer, DWORD nOutBufferSize)
1573 {
1574     DWORD       sz = 0;
1575     NTSTATUS    status = STATUS_SUCCESS;
1576     int fd, dev;
1577
1578     TRACE("%lx %s %lx %ld %lx %ld %p\n",
1579           (DWORD)hDevice, iocodex(dwIoControlCode), (DWORD)lpInBuffer, nInBufferSize,
1580           (DWORD)lpOutBuffer, nOutBufferSize, piosb);
1581
1582     piosb->Information = 0;
1583
1584     if ((status = wine_server_handle_to_fd( hDevice, 0, &fd, NULL, NULL ))) goto error;
1585     if ((status = CDROM_Open(fd, &dev)))
1586     {
1587         wine_server_release_fd( hDevice, fd );
1588         goto error;
1589     }
1590
1591     switch (dwIoControlCode)
1592     {
1593     case IOCTL_STORAGE_CHECK_VERIFY:
1594     case IOCTL_CDROM_CHECK_VERIFY:
1595         sz = 0;
1596         CDROM_ClearCacheEntry(dev);
1597         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1598             status = STATUS_INVALID_PARAMETER;
1599         else status = CDROM_Verify(dev, fd);
1600         break;
1601
1602 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
1603
1604 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
1605 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
1606
1607     case IOCTL_STORAGE_LOAD_MEDIA:
1608     case IOCTL_CDROM_LOAD_MEDIA:
1609         sz = 0;
1610         CDROM_ClearCacheEntry(dev);
1611         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1612             status = STATUS_INVALID_PARAMETER;
1613         else status = CDROM_SetTray(fd, FALSE);
1614         break;
1615      case IOCTL_STORAGE_EJECT_MEDIA:
1616         sz = 0;
1617         CDROM_ClearCacheEntry(dev);
1618         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1619             status = STATUS_INVALID_PARAMETER;
1620         else status = CDROM_SetTray(fd, TRUE);
1621         break;
1622
1623     case IOCTL_CDROM_MEDIA_REMOVAL:
1624     case IOCTL_DISK_MEDIA_REMOVAL:
1625     case IOCTL_STORAGE_MEDIA_REMOVAL:
1626     case IOCTL_STORAGE_EJECTION_CONTROL:
1627         /* FIXME the last ioctl:s is not the same as the two others...
1628          * lockcount/owner should be handled */
1629         sz = 0;
1630         CDROM_ClearCacheEntry(dev);
1631         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1632         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL;
1633         else status = CDROM_ControlEjection(fd, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1634         break;
1635
1636 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
1637
1638     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1639         sz = sizeof(STORAGE_DEVICE_NUMBER);
1640         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1641         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1642         else status = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
1643         break;
1644
1645     case IOCTL_STORAGE_RESET_DEVICE:
1646         sz = 0;
1647         CDROM_ClearCacheEntry(dev);
1648         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1649             status = STATUS_INVALID_PARAMETER;
1650         else status = CDROM_ResetAudio(fd);
1651         break;
1652
1653     case IOCTL_CDROM_GET_CONTROL:
1654         sz = sizeof(CDROM_AUDIO_CONTROL);
1655         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1656         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1657         else status = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
1658         break;
1659
1660     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1661         sz = sizeof(DISK_GEOMETRY);
1662         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1663         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1664         else status = CDROM_GetDriveGeometry(dev, fd, (DISK_GEOMETRY*)lpOutBuffer);
1665         break;
1666
1667     case IOCTL_CDROM_DISK_TYPE:
1668         sz = sizeof(CDROM_DISK_DATA);
1669         /* CDROM_ClearCacheEntry(dev); */
1670         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1671         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1672         else status = CDROM_GetDiskData(dev, fd, (CDROM_DISK_DATA*)lpOutBuffer);
1673         break;
1674
1675 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
1676
1677     case IOCTL_CDROM_READ_Q_CHANNEL:
1678         sz = sizeof(SUB_Q_CHANNEL_DATA);
1679         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
1680             status = STATUS_INVALID_PARAMETER;
1681         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1682         else status = CDROM_ReadQChannel(dev, fd, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1683                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1684         break;
1685
1686     case IOCTL_CDROM_READ_TOC:
1687         sz = sizeof(CDROM_TOC);
1688         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1689         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1690         else status = CDROM_ReadTOC(dev, fd, (CDROM_TOC*)lpOutBuffer);
1691         break;
1692
1693 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
1694
1695     case IOCTL_CDROM_PAUSE_AUDIO:
1696         sz = 0;
1697         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1698             status = STATUS_INVALID_PARAMETER;
1699         else status = CDROM_PauseAudio(fd);
1700         break;
1701     case IOCTL_CDROM_PLAY_AUDIO_MSF:
1702         sz = 0;
1703         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1704         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
1705         else status = CDROM_PlayAudioMSF(fd, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1706         break;
1707     case IOCTL_CDROM_RESUME_AUDIO:
1708         sz = 0;
1709         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1710             status = STATUS_INVALID_PARAMETER;
1711         else status = CDROM_ResumeAudio(fd);
1712         break;
1713     case IOCTL_CDROM_SEEK_AUDIO_MSF:
1714         sz = 0;
1715         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1716         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
1717         else status = CDROM_SeekAudioMSF(dev, fd, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1718         break;
1719     case IOCTL_CDROM_STOP_AUDIO:
1720         sz = 0;
1721         CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
1722         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1723             status = STATUS_INVALID_PARAMETER;
1724         else status = CDROM_StopAudio(fd);
1725         break;
1726     case IOCTL_CDROM_GET_VOLUME:
1727         sz = sizeof(VOLUME_CONTROL);
1728         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1729         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1730         else status = CDROM_GetVolume(fd, (VOLUME_CONTROL*)lpOutBuffer);
1731         break;
1732     case IOCTL_CDROM_SET_VOLUME:
1733         sz = 0;
1734         CDROM_ClearCacheEntry(dev);
1735         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1736             status = STATUS_INVALID_PARAMETER;
1737         else status = CDROM_SetVolume(fd, (const VOLUME_CONTROL*)lpInBuffer);
1738         break;
1739     case IOCTL_CDROM_RAW_READ:
1740         sz = 0;
1741         if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER;
1742         else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL;
1743         else status = CDROM_RawRead(fd, (const RAW_READ_INFO*)lpInBuffer,
1744                                    lpOutBuffer, nOutBufferSize, &sz);
1745         break;
1746     case IOCTL_SCSI_GET_ADDRESS:
1747         sz = sizeof(SCSI_ADDRESS);
1748         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1749         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1750         else status = CDROM_GetAddress(fd, (SCSI_ADDRESS*)lpOutBuffer);
1751         break;
1752     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
1753         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
1754         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1755         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL;
1756         else status = CDROM_ScsiPassThroughDirect(fd, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1757         break;
1758     case IOCTL_SCSI_PASS_THROUGH:
1759         sz = sizeof(SCSI_PASS_THROUGH);
1760         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1761         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL;
1762         else status = CDROM_ScsiPassThrough(fd, (PSCSI_PASS_THROUGH)lpOutBuffer);
1763         break;
1764     case IOCTL_SCSI_GET_CAPABILITIES:
1765         sz = sizeof(IO_SCSI_CAPABILITIES);
1766         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1767         else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL;
1768         else status = CDROM_ScsiGetCaps((PIO_SCSI_CAPABILITIES)lpOutBuffer);
1769         break;
1770     default:
1771         FIXME("Unsupported IOCTL %lx (type=%lx access=%lx func=%lx meth=%lx)\n", 
1772               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
1773               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
1774         sz = 0;
1775         status = STATUS_INVALID_PARAMETER;
1776         break;
1777     }
1778     wine_server_release_fd( hDevice, fd );
1779  error:
1780     piosb->u.Status = status;
1781     piosb->Information = sz;
1782     if (hEvent) NtSetEvent(hEvent, NULL);
1783     return status;
1784 }