oledlg: Post OLEUI_MSG_HELP when the Help button is pressed and hide the Help button...
[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  * Copyright 2005 Ivan Leo Puoti
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <errno.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #ifdef HAVE_IO_H
32 # include <io.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <fcntl.h>
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
40 #endif
41 #include <sys/types.h>
42
43 #ifdef HAVE_SYS_IOCTL_H
44 #include <sys/ioctl.h>
45 #endif
46 #ifdef HAVE_SCSI_SG_H
47 # include <scsi/sg.h>
48 #endif
49 #ifdef HAVE_SCSI_SCSI_H
50 # include <scsi/scsi.h>
51 # undef REASSIGN_BLOCKS  /* avoid conflict with winioctl.h */
52 #endif
53 #ifdef HAVE_SCSI_SCSI_IOCTL_H
54 # include <scsi/scsi_ioctl.h>
55 #endif
56 #ifdef HAVE_LINUX_MAJOR_H
57 # include <linux/major.h>
58 #endif
59 #ifdef HAVE_LINUX_HDREG_H
60 # include <linux/hdreg.h>
61 #endif
62 #ifdef HAVE_LINUX_PARAM_H
63 # include <linux/param.h>
64 #endif
65 #ifdef HAVE_LINUX_CDROM_H
66 # include <linux/cdrom.h>
67 #endif
68 #ifdef HAVE_LINUX_UCDROM_H
69 # include <linux/ucdrom.h>
70 #endif
71 #ifdef HAVE_SYS_CDIO_H
72 # include <sys/cdio.h>
73 #endif
74 #ifdef HAVE_SYS_SCSIIO_H
75 # include <sys/scsiio.h>
76 #endif
77
78 #ifdef HAVE_IOKIT_IOKITLIB_H
79 # ifndef SENSEBUFLEN
80 #  include <IOKit/IOKitLib.h>
81 #  include <IOKit/scsi/SCSICmds_REQUEST_SENSE_Defs.h>
82 #  define SENSEBUFLEN kSenseDefaultSize
83 # endif
84 #endif
85
86 #define NONAMELESSUNION
87 #define NONAMELESSSTRUCT
88 #include "ntstatus.h"
89 #define WIN32_NO_STATUS
90 #include "windef.h"
91 #include "winternl.h"
92 #include "winioctl.h"
93 #include "ntddstor.h"
94 #include "ntddcdrm.h"
95 #include "ddk/ntddcdvd.h"
96 #include "ntddscsi.h"
97 #include "ntdll_misc.h"
98 #include "wine/server.h"
99 #include "wine/library.h"
100 #include "wine/debug.h"
101
102 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
103
104 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
105    lack the following constants. */
106
107 #ifndef CD_SECS
108 # define CD_SECS              60 /* seconds per minute */
109 #endif
110 #ifndef CD_FRAMES
111 # define CD_FRAMES            75 /* frames per second */
112 #endif
113
114 static const struct iocodexs
115 {
116   DWORD code;
117   const char *codex;
118 } iocodextable[] = {
119 #define X(x) { x, #x },
120 X(IOCTL_CDROM_CHECK_VERIFY)
121 X(IOCTL_CDROM_CURRENT_POSITION)
122 X(IOCTL_CDROM_DISK_TYPE)
123 X(IOCTL_CDROM_GET_CONTROL)
124 X(IOCTL_CDROM_GET_DRIVE_GEOMETRY)
125 X(IOCTL_CDROM_GET_VOLUME)
126 X(IOCTL_CDROM_LOAD_MEDIA)
127 X(IOCTL_CDROM_MEDIA_CATALOG)
128 X(IOCTL_CDROM_MEDIA_REMOVAL)
129 X(IOCTL_CDROM_PAUSE_AUDIO)
130 X(IOCTL_CDROM_PLAY_AUDIO_MSF)
131 X(IOCTL_CDROM_RAW_READ)
132 X(IOCTL_CDROM_READ_Q_CHANNEL)
133 X(IOCTL_CDROM_READ_TOC)
134 X(IOCTL_CDROM_RESUME_AUDIO)
135 X(IOCTL_CDROM_SEEK_AUDIO_MSF)
136 X(IOCTL_CDROM_SET_VOLUME)
137 X(IOCTL_CDROM_STOP_AUDIO)
138 X(IOCTL_CDROM_TRACK_ISRC)
139 X(IOCTL_DISK_MEDIA_REMOVAL)
140 X(IOCTL_DVD_END_SESSION)
141 X(IOCTL_DVD_GET_REGION)
142 X(IOCTL_DVD_READ_KEY)
143 X(IOCTL_DVD_READ_STRUCTURE)
144 X(IOCTL_DVD_SEND_KEY)
145 X(IOCTL_DVD_START_SESSION)
146 X(IOCTL_SCSI_GET_ADDRESS)
147 X(IOCTL_SCSI_GET_CAPABILITIES)
148 X(IOCTL_SCSI_GET_INQUIRY_DATA)
149 X(IOCTL_SCSI_PASS_THROUGH)
150 X(IOCTL_SCSI_PASS_THROUGH_DIRECT)
151 X(IOCTL_STORAGE_CHECK_VERIFY)
152 X(IOCTL_STORAGE_EJECTION_CONTROL)
153 X(IOCTL_STORAGE_EJECT_MEDIA)
154 X(IOCTL_STORAGE_GET_DEVICE_NUMBER)
155 X(IOCTL_STORAGE_LOAD_MEDIA)
156 X(IOCTL_STORAGE_MEDIA_REMOVAL)
157 X(IOCTL_STORAGE_RESET_DEVICE)
158 #undef X
159 };
160 static const char *iocodex(DWORD code)
161 {
162    unsigned int i;
163    static char buffer[25];
164    for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
165       if (code==iocodextable[i].code)
166          return iocodextable[i].codex;
167    sprintf(buffer, "IOCTL_CODE_%x", (int)code);
168    return buffer;
169 }
170
171 #define INQ_REPLY_LEN 36
172 #define INQ_CMD_LEN 6
173
174 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
175 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
176 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
177 #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;}
178
179 /* The documented format of DVD_LAYER_DESCRIPTOR is wrong. Even the format in the
180  * DDK's header is wrong. There are four bytes at the start which always seem to
181  * follow the sequence "02 08 00 00".
182  */
183 typedef struct
184 {
185     UCHAR MagicHeader[4];
186     UCHAR BookVersion : 4;
187     UCHAR BookType : 4;
188     UCHAR MinimumRate : 4;
189     UCHAR DiskSize : 4;
190     UCHAR LayerType : 4;
191     UCHAR TrackPath : 1;
192     UCHAR NumberOfLayers : 2;
193     UCHAR Reserved1 : 1;
194     UCHAR TrackDensity : 4;
195     UCHAR LinearDensity : 4;
196     ULONG StartingDataSector;
197     ULONG EndDataSector;
198     ULONG EndLayerZeroSector;
199     UCHAR Reserved5 : 7;
200     UCHAR BCAFlag : 1;
201     UCHAR Reserved6;
202 } internal_dvd_layer_descriptor;
203
204
205 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
206 static NTSTATUS CDROM_GetStatusCode(int);
207
208
209 #ifdef linux
210
211 # ifndef IDE6_MAJOR
212 #  define IDE6_MAJOR 88
213 # endif
214 # ifndef IDE7_MAJOR
215 #  define IDE7_MAJOR 89
216 # endif
217
218 # ifdef CDROM_SEND_PACKET
219 /* structure for CDROM_PACKET_COMMAND ioctl */
220 /* not all Linux versions have all the fields, so we define the
221  * structure ourselves to make sure */
222 struct linux_cdrom_generic_command
223 {
224     unsigned char          cmd[CDROM_PACKET_SIZE];
225     unsigned char         *buffer;
226     unsigned int           buflen;
227     int                    stat;
228     struct request_sense  *sense;
229     unsigned char          data_direction;
230     int                    quiet;
231     int                    timeout;
232     void                  *reserved[1];
233 };
234 # endif  /* CDROM_SEND_PACKET */
235
236 #endif  /* linux */
237
238 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
239  * this should be removed when a proper device interface is implemented
240  * 
241  * (WS) We need this to keep track of current position and to safely
242  * detect media changes. Besides this should provide a great speed up
243  * for toc inquiries.
244  */
245 struct cdrom_cache {
246     dev_t       device;
247     ino_t       inode;
248     char        toc_good; /* if false, will reread TOC from disk */
249     CDROM_TOC   toc;
250     SUB_Q_CURRENT_POSITION CurrentPosition;
251 };
252 /* who has more than 5 cdroms on his/her machine ?? */
253 /* FIXME: this should grow depending on the number of cdroms we install/configure 
254  * at startup
255  */
256 #define MAX_CACHE_ENTRIES       5
257 static struct cdrom_cache cdrom_cache[MAX_CACHE_ENTRIES];
258
259 static RTL_CRITICAL_SECTION cache_section;
260 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
261 {
262     0, 0, &cache_section,
263     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
264       0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
265 };
266 static RTL_CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
267
268 /* Proposed media change function: not really needed at this time */
269 /* This is a 1 or 0 type of function */
270 #if 0
271 static int CDROM_MediaChanged(int dev)
272 {
273    int i;
274
275    struct cdrom_tochdr  hdr;
276    struct cdrom_tocentry entry;
277
278    if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
279       return 0;
280    if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
281       return 0;
282
283    if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
284       return 1;
285
286    for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
287    {
288       if (i == hdr.cdth_trk1 + 1)
289       {
290          entry.cdte_track = CDROM_LEADOUT;
291       } else {
292          entry.cdte_track = i;
293       }
294       entry.cdte_format = CDROM_MSF;
295       if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
296          return 0;
297       if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
298                               sizeof(struct cdrom_tocentry)) )
299          return 1;
300    }
301    return 0;
302 }
303 #endif
304
305 /******************************************************************
306  *              CDROM_SyncCache                          [internal]
307  *
308  * Read the TOC in and store it in the cdrom_cache structure.
309  * Further requests for the TOC will be copied from the cache
310  * unless certain events like disk ejection is detected, in which
311  * case the cache will be cleared, causing it to be resynced.
312  * The cache section must be held by caller.
313  */
314 static NTSTATUS CDROM_SyncCache(int dev, int fd)
315 {
316 #ifdef linux
317    int i, tsz;
318    struct cdrom_tochdr          hdr;
319    struct cdrom_tocentry        entry;
320
321    CDROM_TOC *toc = &cdrom_cache[dev].toc;
322    cdrom_cache[dev].toc_good = 0;
323
324    if (ioctl(fd, CDROMREADTOCHDR, &hdr) == -1)
325    {
326       WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
327       return FILE_GetNtStatus();
328    }
329
330    toc->FirstTrack = hdr.cdth_trk0;
331    toc->LastTrack  = hdr.cdth_trk1;
332    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
333        + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
334    toc->Length[0] = tsz >> 8;
335    toc->Length[1] = tsz;
336
337    TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
338
339    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
340    {
341      if (i == toc->LastTrack + 1)
342        entry.cdte_track = CDROM_LEADOUT;
343      else 
344        entry.cdte_track = i;
345      entry.cdte_format = CDROM_MSF;
346      if (ioctl(fd, CDROMREADTOCENTRY, &entry) == -1)
347      {
348        WARN("error read entry (%s)\n", strerror(errno));
349        return FILE_GetNtStatus();
350      }
351      toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
352      toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
353      /* marking last track with leadout value as index */
354      toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
355      toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
356      toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
357      toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
358      toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
359    }
360    cdrom_cache[dev].toc_good = 1;
361    return STATUS_SUCCESS;
362
363 #elif defined(__FreeBSD__) || defined(__NetBSD__)
364
365    int i, tsz;
366    struct ioc_toc_header hdr;
367    struct ioc_read_toc_entry entry;
368    struct cd_toc_entry toc_buffer;
369
370    CDROM_TOC *toc = &cdrom_cache[dev].toc;
371    cdrom_cache[dev].toc_good = 0;
372
373     if (ioctl(fd, CDIOREADTOCHEADER, &hdr) == -1)
374     {
375         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
376         return FILE_GetNtStatus();
377     }
378     toc->FirstTrack = hdr.starting_track;
379     toc->LastTrack  = hdr.ending_track;
380     tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
381         + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
382     toc->Length[0] = tsz >> 8;
383     toc->Length[1] = tsz;
384
385     TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
386
387     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
388     {
389         if (i == toc->LastTrack + 1)
390         {
391 #define LEADOUT 0xaa
392             entry.starting_track = LEADOUT;
393         } else {
394             entry.starting_track = i;
395         }
396         memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
397         entry.address_format = CD_MSF_FORMAT;
398         entry.data_len = sizeof(toc_buffer);
399         entry.data = &toc_buffer;
400         if (ioctl(fd, CDIOREADTOCENTRYS, &entry) == -1)
401         {
402             WARN("error read entry (%s)\n", strerror(errno));
403             return FILE_GetNtStatus();
404         }
405         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
406         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
407         /* marking last track with leadout value as index */
408         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
409         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
410         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
411         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
412         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
413     }
414     cdrom_cache[dev].toc_good = 1;
415     return STATUS_SUCCESS;
416 #else
417     return STATUS_NOT_SUPPORTED;
418 #endif
419 }
420
421 static void CDROM_ClearCacheEntry(int dev)
422 {
423     RtlEnterCriticalSection( &cache_section );
424     cdrom_cache[dev].toc_good = 0;
425     RtlLeaveCriticalSection( &cache_section );
426 }
427
428
429
430 /******************************************************************
431  *              CDROM_GetInterfaceInfo
432  *
433  * Determines the ide interface (the number after the ide), and the
434  * number of the device on that interface for ide cdroms (*iface <= 1).
435  * Determines the scsi information for scsi cdroms (*iface >= 2).
436  * Returns false if the info cannot not be obtained.
437  */
438 static int CDROM_GetInterfaceInfo(int fd, UCHAR* iface, UCHAR* port, UCHAR* device, UCHAR* lun)
439 {
440 #if defined(linux)
441     struct stat st;
442     if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) return 0;
443     *port = 0;
444     *iface = 0;
445     *device = 0;
446     *lun = 0;
447     switch (major(st.st_rdev)) {
448     case IDE0_MAJOR: *iface = 0; break;
449     case IDE1_MAJOR: *iface = 1; break;
450     case IDE2_MAJOR: *iface = 2; break;
451     case IDE3_MAJOR: *iface = 3; break;
452     case IDE4_MAJOR: *iface = 4; break;
453     case IDE5_MAJOR: *iface = 5; break;
454     case IDE6_MAJOR: *iface = 6; break;
455     case IDE7_MAJOR: *iface = 7; break;
456     default: *port = 1; break;
457     }
458
459     if (*port == 0)
460         *device = (minor(st.st_rdev) >> 6);
461     else
462     {
463 #ifdef SCSI_IOCTL_GET_IDLUN
464         UINT32 idlun[2];
465         if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun) != -1)
466         {
467             *port = (idlun[0] >> 24) & 0xff;
468             *iface = ((idlun[0] >> 16) & 0xff) + 2;
469             *device = idlun[0] & 0xff;
470             *lun = (idlun[0] >> 8) & 0xff;
471         }
472         else
473 #endif
474         {
475             WARN("CD-ROM device (%d, %d) not supported\n", major(st.st_rdev), minor(st.st_rdev));
476             return 0;
477         }
478     }
479     return 1;
480 #elif defined(__NetBSD__)
481     struct scsi_addr addr;
482     if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1)
483     {
484         switch (addr.type) 
485         {
486         case TYPE_SCSI:  *port = 1;
487             *iface = addr.addr.scsi.scbus;
488             *device = addr.addr.scsi.target;
489             *lun = addr.addr.scsi.lun;
490             break;
491         case TYPE_ATAPI: *port = 0;
492             *iface = addr.addr.atapi.atbus;
493             *device = addr.addr.atapi.drive;
494             *lun = 0;
495             break;
496         }
497         return 1;
498     }
499     return 0;
500 #elif defined(__FreeBSD__)
501     FIXME("not implemented for BSD\n");
502     return 0;
503 #else
504     FIXME("not implemented for nonlinux\n");
505     return 0;
506 #endif
507 }
508
509
510 /******************************************************************
511  *              CDROM_Open
512  *
513  */
514 static NTSTATUS CDROM_Open(int fd, int* dev)
515 {
516     struct stat st;
517     NTSTATUS ret = STATUS_SUCCESS;
518     int         empty = -1;
519
520     fstat(fd, &st);
521
522     RtlEnterCriticalSection( &cache_section );
523     for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++)
524     {
525         if (empty == -1 &&
526             cdrom_cache[*dev].device == 0 &&
527             cdrom_cache[*dev].inode == 0)
528             empty = *dev;
529         else if (cdrom_cache[*dev].device == st.st_dev &&
530                  cdrom_cache[*dev].inode == st.st_ino)
531             break;
532     }
533     if (*dev == MAX_CACHE_ENTRIES)
534     {
535         if (empty == -1) ret = STATUS_NOT_IMPLEMENTED;
536         else
537         {
538             *dev = empty;
539             cdrom_cache[*dev].device  = st.st_dev;
540             cdrom_cache[*dev].inode   = st.st_ino;
541         }
542     }
543     RtlLeaveCriticalSection( &cache_section );
544
545     TRACE("%d, %d\n", *dev, fd);
546     return ret;
547 }
548
549 /******************************************************************
550  *              CDROM_GetStatusCode
551  *
552  *
553  */
554 static NTSTATUS CDROM_GetStatusCode(int io)
555 {
556     if (io == 0) return STATUS_SUCCESS;
557     return FILE_GetNtStatus();
558 }
559
560 /******************************************************************
561  *              CDROM_GetControl
562  *
563  */
564 static NTSTATUS CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
565 {
566     cac->LbaFormat = 0; /* FIXME */
567     cac->LogicalBlocksPerSecond = 1; /* FIXME */
568     return  STATUS_NOT_SUPPORTED;
569 }
570
571 /******************************************************************
572  *              CDROM_GetDeviceNumber
573  *
574  */
575 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
576 {
577     return STATUS_NOT_SUPPORTED;
578 }
579
580 /******************************************************************
581  *              CDROM_GetDriveGeometry
582  *
583  */
584 static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg)
585 {
586   CDROM_TOC     toc;
587   NTSTATUS      ret = 0;
588   int           fsize = 0;
589
590   if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
591
592   fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
593         - FRAME_OF_TOC(toc, 1); /* Total size in frames */
594   
595   dg->Cylinders.u.LowPart = fsize / (64 * 32); 
596   dg->Cylinders.u.HighPart = 0; 
597   dg->MediaType = RemovableMedia;  
598   dg->TracksPerCylinder = 64; 
599   dg->SectorsPerTrack = 32;  
600   dg->BytesPerSector= 2048; 
601   return ret;
602 }
603
604 /**************************************************************************
605  *                              CDROM_Reset                     [internal]
606  */
607 static NTSTATUS CDROM_ResetAudio(int fd)
608 {
609 #if defined(linux)
610     return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
611 #elif defined(__FreeBSD__) || defined(__NetBSD__)
612     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
613 #else
614     return STATUS_NOT_SUPPORTED;
615 #endif
616 }
617
618 /******************************************************************
619  *              CDROM_SetTray
620  *
621  *
622  */
623 static NTSTATUS CDROM_SetTray(int fd, BOOL doEject)
624 {
625 #if defined(linux)
626     return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
627 #elif defined(__FreeBSD__) || defined(__NetBSD__)
628     return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
629                                (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
630                                (ioctl(fd, CDIOCPREVENT, NULL)));
631 #else
632     return STATUS_NOT_SUPPORTED;
633 #endif
634 }
635
636 /******************************************************************
637  *              CDROM_ControlEjection
638  *
639  *
640  */
641 static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv)
642 {
643 #if defined(linux)
644     return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
645 #elif defined(__FreeBSD__) || defined(__NetBSD__)
646     return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
647 #else
648     return STATUS_NOT_SUPPORTED;
649 #endif
650 }
651
652 /******************************************************************
653  *              CDROM_ReadTOC
654  *
655  *
656  */
657 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
658 {
659     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
660
661     if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
662         return STATUS_INVALID_PARAMETER;
663
664     RtlEnterCriticalSection( &cache_section );
665     if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd)))
666     {
667         *toc = cdrom_cache[dev].toc;
668         ret = STATUS_SUCCESS;
669     }
670     RtlLeaveCriticalSection( &cache_section );
671     return ret;
672 }
673
674 /******************************************************************
675  *              CDROM_GetDiskData
676  *
677  *
678  */
679 static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data)
680 {
681     CDROM_TOC   toc;
682     NTSTATUS    ret;
683     int         i;
684
685     if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
686     data->DiskData = 0;
687     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
688         if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
689             data->DiskData |= CDROM_DISK_DATA_TRACK;
690         else
691             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
692     }
693     return STATUS_SUCCESS;
694 }
695
696 /******************************************************************
697  *              CDROM_ReadQChannel
698  *
699  *
700  */
701 static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt,
702                                    SUB_Q_CHANNEL_DATA* data)
703 {
704     NTSTATUS            ret = STATUS_NOT_SUPPORTED;
705 #ifdef linux
706     unsigned            size;
707     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
708     int                 io;
709     struct cdrom_subchnl        sc;
710     sc.cdsc_format = CDROM_MSF;
711
712     io = ioctl(fd, CDROMSUBCHNL, &sc);
713     if (io == -1)
714     {
715         TRACE("opened or no_media (%s)!\n", strerror(errno));
716         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
717         CDROM_ClearCacheEntry(dev);
718         goto end;
719     }
720
721     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
722
723     switch (sc.cdsc_audiostatus) {
724     case CDROM_AUDIO_INVALID:
725         CDROM_ClearCacheEntry(dev);
726         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
727         break;
728     case CDROM_AUDIO_NO_STATUS:
729         CDROM_ClearCacheEntry(dev);
730         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
731         break;
732     case CDROM_AUDIO_PLAY:
733         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
734         break;
735     case CDROM_AUDIO_PAUSED:
736         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
737         break;
738     case CDROM_AUDIO_COMPLETED:
739         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
740         break;
741     case CDROM_AUDIO_ERROR:
742         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
743         break;
744     default:
745         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
746         break;
747     }
748     switch (fmt->Format)
749     {
750     case IOCTL_CDROM_CURRENT_POSITION:
751         size = sizeof(SUB_Q_CURRENT_POSITION);
752         RtlEnterCriticalSection( &cache_section );
753         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
754           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
755           data->CurrentPosition.Control = sc.cdsc_ctrl; 
756           data->CurrentPosition.ADR = sc.cdsc_adr; 
757           data->CurrentPosition.TrackNumber = sc.cdsc_trk; 
758           data->CurrentPosition.IndexNumber = sc.cdsc_ind; 
759
760           data->CurrentPosition.AbsoluteAddress[0] = 0; 
761           data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; 
762           data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
763           data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
764  
765           data->CurrentPosition.TrackRelativeAddress[0] = 0; 
766           data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; 
767           data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
768           data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
769
770           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
771         }
772         else /* not playing */
773         {
774           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
775           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
776         }
777         RtlLeaveCriticalSection( &cache_section );
778         break;
779     case IOCTL_CDROM_MEDIA_CATALOG:
780         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
781         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
782         {
783             struct cdrom_mcn mcn;
784             if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
785
786             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
787             data->MediaCatalog.Mcval = 0; /* FIXME */
788             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
789             data->MediaCatalog.MediaCatalog[14] = 0;
790         }
791         break;
792     case IOCTL_CDROM_TRACK_ISRC:
793         size = sizeof(SUB_Q_CURRENT_POSITION);
794         FIXME("TrackIsrc: NIY on linux\n");
795         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
796         data->TrackIsrc.Tcval = 0;
797         io = 0;
798         break;
799     }
800
801  end:
802     ret = CDROM_GetStatusCode(io);
803 #elif defined(__FreeBSD__) || defined(__NetBSD__)
804     unsigned            size;
805     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
806     int                 io;
807     struct ioc_read_subchannel  read_sc;
808     struct cd_sub_channel_info  sc;
809
810     read_sc.address_format = CD_MSF_FORMAT;
811     read_sc.track          = 0;
812     read_sc.data_len       = sizeof(sc);
813     read_sc.data           = &sc;
814     switch (fmt->Format)
815     {
816     case IOCTL_CDROM_CURRENT_POSITION:
817         read_sc.data_format    = CD_CURRENT_POSITION;
818         break;
819     case IOCTL_CDROM_MEDIA_CATALOG:
820         read_sc.data_format    = CD_MEDIA_CATALOG;
821         break;
822     case IOCTL_CDROM_TRACK_ISRC:
823         read_sc.data_format    = CD_TRACK_INFO;
824         sc.what.track_info.track_number = data->TrackIsrc.Track;
825         break;
826     }
827     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
828     if (io == -1)
829     {
830         TRACE("opened or no_media (%s)!\n", strerror(errno));
831         CDROM_ClearCacheEntry(dev);
832         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
833         goto end;
834     }
835
836     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
837
838     switch (sc.header.audio_status) {
839     case CD_AS_AUDIO_INVALID:
840         CDROM_ClearCacheEntry(dev);
841         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
842         break;
843     case CD_AS_NO_STATUS:
844         CDROM_ClearCacheEntry(dev);
845         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
846         break;
847     case CD_AS_PLAY_IN_PROGRESS:
848         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
849         break;
850     case CD_AS_PLAY_PAUSED:
851         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
852         break;
853     case CD_AS_PLAY_COMPLETED:
854         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
855         break;
856     case CD_AS_PLAY_ERROR:
857         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
858         break;
859     default:
860         TRACE("status=%02X !\n", sc.header.audio_status);
861     }
862     switch (fmt->Format)
863     {
864     case IOCTL_CDROM_CURRENT_POSITION:
865         size = sizeof(SUB_Q_CURRENT_POSITION);
866         RtlEnterCriticalSection( &cache_section );
867         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
868           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
869           data->CurrentPosition.Control = sc.what.position.control;
870           data->CurrentPosition.ADR = sc.what.position.addr_type;
871           data->CurrentPosition.TrackNumber = sc.what.position.track_number;
872           data->CurrentPosition.IndexNumber = sc.what.position.index_number;
873
874           data->CurrentPosition.AbsoluteAddress[0] = 0;
875           data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
876           data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
877           data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
878           data->CurrentPosition.TrackRelativeAddress[0] = 0;
879           data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
880           data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
881           data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
882           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
883         }
884         else { /* not playing */
885           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
886           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
887         }
888         RtlLeaveCriticalSection( &cache_section );
889         break;
890     case IOCTL_CDROM_MEDIA_CATALOG:
891         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
892         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
893         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
894         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
895         break;
896     case IOCTL_CDROM_TRACK_ISRC:
897         size = sizeof(SUB_Q_CURRENT_POSITION);
898         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
899         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
900         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
901         break;
902     }
903
904  end:
905     ret = CDROM_GetStatusCode(io);
906 #endif
907     return ret;
908 }
909
910 /******************************************************************
911  *              CDROM_Verify
912  *  Implements: IOCTL_STORAGE_CHECK_VERIFY
913  *              IOCTL_CDROM_CHECK_VERIFY
914  *              IOCTL_DISK_CHECK_VERIFY
915  *
916  */
917 static NTSTATUS CDROM_Verify(int dev, int fd)
918 {
919 #if defined(linux)
920     int ret;
921
922     ret = ioctl(fd,  CDROM_DRIVE_STATUS, NULL);
923     if(ret == -1) {
924         TRACE("ioctl CDROM_DRIVE_STATUS failed(%s)!\n", strerror(errno));
925         return CDROM_GetStatusCode(ret);
926     }
927
928     if(ret == CDS_DISC_OK)
929         return STATUS_SUCCESS;
930     else
931         return STATUS_NO_MEDIA_IN_DEVICE;
932 #endif
933     FIXME("not implemented for non-linux\n");
934     return STATUS_NOT_SUPPORTED;
935 }
936
937 /******************************************************************
938  *              CDROM_PlayAudioMSF
939  *
940  *
941  */
942 static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf)
943 {
944     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
945 #ifdef linux
946     struct      cdrom_msf       msf;
947     int         io;
948
949     msf.cdmsf_min0   = audio_msf->StartingM;
950     msf.cdmsf_sec0   = audio_msf->StartingS;
951     msf.cdmsf_frame0 = audio_msf->StartingF;
952     msf.cdmsf_min1   = audio_msf->EndingM;
953     msf.cdmsf_sec1   = audio_msf->EndingS;
954     msf.cdmsf_frame1 = audio_msf->EndingF;
955
956     io = ioctl(fd, CDROMSTART);
957     if (io == -1)
958     {
959         WARN("motor doesn't start !\n");
960         goto end;
961     }
962     io = ioctl(fd, CDROMPLAYMSF, &msf);
963     if (io == -1)
964     {
965         WARN("device doesn't play !\n");
966         goto end;
967     }
968     TRACE("msf = %d:%d:%d %d:%d:%d\n",
969           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
970           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
971  end:
972     ret = CDROM_GetStatusCode(io);
973 #elif defined(__FreeBSD__) || defined(__NetBSD__)
974     struct      ioc_play_msf    msf;
975     int         io;
976
977     msf.start_m      = audio_msf->StartingM;
978     msf.start_s      = audio_msf->StartingS;
979     msf.start_f      = audio_msf->StartingF;
980     msf.end_m        = audio_msf->EndingM;
981     msf.end_s        = audio_msf->EndingS;
982     msf.end_f        = audio_msf->EndingF;
983
984     io = ioctl(fd, CDIOCSTART, NULL);
985     if (io == -1)
986     {
987         WARN("motor doesn't start !\n");
988         goto end;
989     }
990     io = ioctl(fd, CDIOCPLAYMSF, &msf);
991     if (io == -1)
992     {
993         WARN("device doesn't play !\n");
994         goto end;
995     }
996     TRACE("msf = %d:%d:%d %d:%d:%d\n",
997           msf.start_m, msf.start_s, msf.start_f,
998           msf.end_m,   msf.end_s,   msf.end_f);
999 end:
1000     ret = CDROM_GetStatusCode(io);
1001 #endif
1002     return ret;
1003 }
1004
1005 /******************************************************************
1006  *              CDROM_SeekAudioMSF
1007  *
1008  *
1009  */
1010 static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1011 {
1012     CDROM_TOC toc;
1013     int i, io, frame;
1014     SUB_Q_CURRENT_POSITION *cp;
1015 #if defined(linux)
1016     struct cdrom_msf0   msf;
1017     struct cdrom_subchnl sc;
1018 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1019     struct ioc_play_msf msf;
1020     struct ioc_read_subchannel  read_sc;
1021     struct cd_sub_channel_info  sc;
1022     int final_frame;
1023 #endif
1024
1025     /* Use the information on the TOC to compute the new current
1026      * position, which is shadowed on the cache. [Portable]. */
1027     frame = FRAME_OF_MSF(*audio_msf);
1028
1029     if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io;
1030
1031     for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1032       if (FRAME_OF_TOC(toc,i)>frame) break;
1033     if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1034       return STATUS_INVALID_PARAMETER;
1035     i--;
1036     RtlEnterCriticalSection( &cache_section );
1037     cp = &cdrom_cache[dev].CurrentPosition;
1038     cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION; 
1039     cp->Control = toc.TrackData[i-toc.FirstTrack].Control; 
1040     cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; 
1041     cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1042     cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1043     cp->AbsoluteAddress[0] = 0; 
1044     cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1045     cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1046     cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1047     frame -= FRAME_OF_TOC(toc,i);
1048     cp->TrackRelativeAddress[0] = 0;
1049     MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
1050     RtlLeaveCriticalSection( &cache_section );
1051
1052     /* If playing, then issue a seek command, otherwise do nothing */
1053 #ifdef linux
1054     sc.cdsc_format = CDROM_MSF;
1055
1056     io = ioctl(fd, CDROMSUBCHNL, &sc);
1057     if (io == -1)
1058     {
1059         TRACE("opened or no_media (%s)!\n", strerror(errno));
1060         CDROM_ClearCacheEntry(dev);
1061         return CDROM_GetStatusCode(io);
1062     }
1063     if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1064     {
1065       msf.minute = audio_msf->M;
1066       msf.second = audio_msf->S;
1067       msf.frame  = audio_msf->F;
1068       return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf));
1069     }
1070     return STATUS_SUCCESS;
1071 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1072     read_sc.address_format = CD_MSF_FORMAT;
1073     read_sc.track          = 0;
1074     read_sc.data_len       = sizeof(sc);
1075     read_sc.data           = &sc;
1076     read_sc.data_format    = CD_CURRENT_POSITION;
1077
1078     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1079     if (io == -1)
1080     {
1081         TRACE("opened or no_media (%s)!\n", strerror(errno));
1082         CDROM_ClearCacheEntry(dev);
1083         return CDROM_GetStatusCode(io);
1084     }
1085     if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) 
1086     {
1087
1088       msf.start_m      = audio_msf->M;
1089       msf.start_s      = audio_msf->S;
1090       msf.start_f      = audio_msf->F;
1091       final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1092       MSF_OF_FRAME(msf.end_m, final_frame);
1093
1094       return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
1095     }
1096     return STATUS_SUCCESS;
1097 #else
1098     return STATUS_NOT_SUPPORTED;
1099 #endif
1100 }
1101
1102 /******************************************************************
1103  *              CDROM_PauseAudio
1104  *
1105  *
1106  */
1107 static NTSTATUS CDROM_PauseAudio(int fd)
1108 {
1109 #if defined(linux)
1110     return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
1111 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1112     return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
1113 #else
1114     return STATUS_NOT_SUPPORTED;
1115 #endif
1116 }
1117
1118 /******************************************************************
1119  *              CDROM_ResumeAudio
1120  *
1121  *
1122  */
1123 static NTSTATUS CDROM_ResumeAudio(int fd)
1124 {
1125 #if defined(linux)
1126     return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
1127 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1128     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
1129 #else
1130     return STATUS_NOT_SUPPORTED;
1131 #endif
1132 }
1133
1134 /******************************************************************
1135  *              CDROM_StopAudio
1136  *
1137  *
1138  */
1139 static NTSTATUS CDROM_StopAudio(int fd)
1140 {
1141 #if defined(linux)
1142     return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
1143 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1144     return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
1145 #else
1146     return STATUS_NOT_SUPPORTED;
1147 #endif
1148 }
1149
1150 /******************************************************************
1151  *              CDROM_GetVolume
1152  *
1153  *
1154  */
1155 static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc)
1156 {
1157 #if defined(linux)
1158     struct cdrom_volctrl volc;
1159     int io;
1160
1161     io = ioctl(fd, CDROMVOLREAD, &volc);
1162     if (io != -1)
1163     {
1164         vc->PortVolume[0] = volc.channel0;
1165         vc->PortVolume[1] = volc.channel1;
1166         vc->PortVolume[2] = volc.channel2;
1167         vc->PortVolume[3] = volc.channel3;
1168     }
1169     return CDROM_GetStatusCode(io);
1170 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1171     struct  ioc_vol     volc;
1172     int io;
1173
1174     io = ioctl(fd, CDIOCGETVOL, &volc);
1175     if (io != -1)
1176     {
1177         vc->PortVolume[0] = volc.vol[0];
1178         vc->PortVolume[1] = volc.vol[1];
1179         vc->PortVolume[2] = volc.vol[2];
1180         vc->PortVolume[3] = volc.vol[3];
1181     }
1182     return CDROM_GetStatusCode(io);
1183 #else
1184     return STATUS_NOT_SUPPORTED;
1185 #endif
1186 }
1187
1188 /******************************************************************
1189  *              CDROM_SetVolume
1190  *
1191  *
1192  */
1193 static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
1194 {
1195 #if defined(linux)
1196     struct cdrom_volctrl volc;
1197
1198     volc.channel0 = vc->PortVolume[0];
1199     volc.channel1 = vc->PortVolume[1];
1200     volc.channel2 = vc->PortVolume[2];
1201     volc.channel3 = vc->PortVolume[3];
1202
1203     return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc));
1204 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1205     struct  ioc_vol     volc;
1206
1207     volc.vol[0] = vc->PortVolume[0];
1208     volc.vol[1] = vc->PortVolume[1];
1209     volc.vol[2] = vc->PortVolume[2];
1210     volc.vol[3] = vc->PortVolume[3];
1211
1212     return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
1213 #else
1214     return STATUS_NOT_SUPPORTED;
1215 #endif
1216 }
1217
1218 /******************************************************************
1219  *              CDROM_RawRead
1220  *
1221  * Some features of this IOCTL are rather poorly documented and
1222  * not really intuitive either:
1223  *
1224  *   1. Although the DiskOffset parameter is meant to be a
1225  *      byte offset into the disk, it is in fact the sector
1226  *      number multiplied by 2048 regardless of the actual
1227  *      sector size.
1228  *
1229  *   2. The least significant 11 bits of DiskOffset are ignored.
1230  *
1231  *   3. The TrackMode parameter has no effect on the sector
1232  *      size. The entire raw sector (i.e. 2352 bytes of data)
1233  *      is always returned. IMO the TrackMode is only used
1234  *      to check the correct sector type.
1235  *
1236  */
1237 static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1238 {
1239     int         ret = STATUS_NOT_SUPPORTED;
1240     int         io = -1;
1241
1242     TRACE("RAW_READ_INFO: DiskOffset=%i,%i SectorCount=%i TrackMode=%i\n buffer=%p len=%i sz=%p\n",
1243           raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz);
1244
1245     if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL;
1246
1247 #if defined(linux)
1248     if (raw->DiskOffset.u.HighPart & ~2047) {
1249         WARN("DiskOffset points to a sector >= 2**32\n");
1250         return ret;
1251     }
1252
1253     switch (raw->TrackMode)
1254     {
1255     case YellowMode2:
1256     case XAForm2:
1257     {
1258         DWORD lba = raw->DiskOffset.QuadPart >> 11;
1259         struct cdrom_msf*       msf;
1260         PBYTE                   *bp; /* current buffer pointer */
1261         int i;
1262
1263         if ((lba + raw->SectorCount) >
1264             ((1 << 8*sizeof(msf->cdmsf_min0)) * CD_SECS * CD_FRAMES
1265              - CD_MSF_OFFSET)) {
1266             WARN("DiskOffset not accessible with MSF\n");
1267             return ret;
1268         }
1269
1270         /* Linux reads only one sector at a time.
1271          * ioctl CDROMREADRAW takes struct cdrom_msf as an argument
1272          * on the contrary to what header comments state.
1273          */
1274         lba += CD_MSF_OFFSET;
1275         for (i = 0, bp = buffer; i < raw->SectorCount;
1276              i++, lba++, bp += 2352)
1277         {
1278             msf = (struct cdrom_msf*)bp;
1279             msf->cdmsf_min0   = lba / CD_FRAMES / CD_SECS;
1280             msf->cdmsf_sec0   = lba / CD_FRAMES % CD_SECS;
1281             msf->cdmsf_frame0 = lba % CD_FRAMES;
1282             io = ioctl(fd, CDROMREADRAW, msf);
1283             if (io != 0)
1284             {
1285                 *sz = 2352 * i;
1286                 return CDROM_GetStatusCode(io);
1287             }
1288         }
1289         break;
1290     }
1291
1292     case CDDA:
1293     {
1294         struct cdrom_read_audio cdra;
1295
1296         cdra.addr.lba = raw->DiskOffset.QuadPart >> 11;
1297         TRACE("reading at %u\n", cdra.addr.lba);
1298         cdra.addr_format = CDROM_LBA;
1299         cdra.nframes = raw->SectorCount;
1300         cdra.buf = buffer;
1301         io = ioctl(fd, CDROMREADAUDIO, &cdra);
1302         break;
1303     }
1304
1305     default:
1306         FIXME("NIY: %d\n", raw->TrackMode);
1307         return STATUS_INVALID_PARAMETER;
1308     }
1309 #else
1310     switch (raw->TrackMode)
1311     {
1312     case YellowMode2:
1313         FIXME("YellowMode2: NIY\n");
1314         return ret;
1315     case XAForm2:
1316         FIXME("XAForm2: NIY\n");
1317         return ret;
1318     case CDDA:
1319         FIXME("CDDA: NIY\n");
1320         return ret;
1321     default:
1322         FIXME("NIY: %d\n", raw->TrackMode);
1323         return STATUS_INVALID_PARAMETER;
1324     }
1325 #endif
1326
1327     *sz = 2352 * raw->SectorCount;
1328     ret = CDROM_GetStatusCode(io);
1329     return ret;
1330 }
1331
1332 /******************************************************************
1333  *        CDROM_ScsiPassThroughDirect
1334  *        Implements IOCTL_SCSI_PASS_THROUGH_DIRECT
1335  *
1336  */
1337 static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket)
1338 {
1339     int ret = STATUS_NOT_SUPPORTED;
1340 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1341     sg_io_hdr_t cmd;
1342     int io;
1343 #elif defined HAVE_SCSIREQ_T_CMD
1344     scsireq_t cmd;
1345     int io;
1346 #endif
1347
1348     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1349         return STATUS_BUFFER_TOO_SMALL;
1350
1351     if (pPacket->CdbLength > 16)
1352         return STATUS_INVALID_PARAMETER;
1353
1354 #ifdef SENSEBUFLEN
1355     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1356         return STATUS_INVALID_PARAMETER;
1357 #elif defined HAVE_REQUEST_SENSE
1358     if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1359         return STATUS_INVALID_PARAMETER;
1360 #endif
1361
1362     if (pPacket->DataTransferLength > 0 && !pPacket->DataBuffer)
1363         return STATUS_INVALID_PARAMETER;
1364
1365 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1366     RtlZeroMemory(&cmd, sizeof(cmd));
1367
1368     cmd.interface_id   = 'S';
1369     cmd.cmd_len        = pPacket->CdbLength;
1370     cmd.mx_sb_len      = pPacket->SenseInfoLength;
1371     cmd.dxfer_len      = pPacket->DataTransferLength;
1372     cmd.dxferp         = pPacket->DataBuffer;
1373     cmd.cmdp           = pPacket->Cdb;
1374     cmd.sbp            = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1375     cmd.timeout        = pPacket->TimeOutValue*1000;
1376
1377     switch (pPacket->DataIn)
1378     {
1379     case SCSI_IOCTL_DATA_IN:
1380         cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1381         break;
1382     case SCSI_IOCTL_DATA_OUT:
1383         cmd.dxfer_direction = SG_DXFER_TO_DEV;
1384         break;
1385     case SCSI_IOCTL_DATA_UNSPECIFIED:
1386         cmd.dxfer_direction = SG_DXFER_NONE;
1387         break;
1388     default:
1389        return STATUS_INVALID_PARAMETER;
1390     }
1391
1392     io = ioctl(fd, SG_IO, &cmd);
1393
1394     pPacket->ScsiStatus         = cmd.status;
1395     pPacket->DataTransferLength = cmd.resid;
1396     pPacket->SenseInfoLength    = cmd.sb_len_wr;
1397
1398     ret = CDROM_GetStatusCode(io);
1399
1400 #elif defined HAVE_SCSIREQ_T_CMD
1401
1402     memset(&cmd, 0, sizeof(cmd));
1403     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1404
1405     cmd.cmdlen         = pPacket->CdbLength;
1406     cmd.databuf        = pPacket->DataBuffer;
1407     cmd.datalen        = pPacket->DataTransferLength;
1408     cmd.senselen       = pPacket->SenseInfoLength;
1409     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1410
1411     switch (pPacket->DataIn)
1412     {
1413     case SCSI_IOCTL_DATA_OUT:
1414         cmd.flags |= SCCMD_WRITE;
1415         break;
1416     case SCSI_IOCTL_DATA_IN:
1417         cmd.flags |= SCCMD_READ;
1418         break;
1419     case SCSI_IOCTL_DATA_UNSPECIFIED:
1420         cmd.flags = 0;
1421         break;
1422     default:
1423        return STATUS_INVALID_PARAMETER;
1424     }
1425
1426     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1427
1428     switch (cmd.retsts)
1429     {
1430     case SCCMD_OK:         break;
1431     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1432                            break;
1433     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1434                            break;
1435     case SCCMD_SENSE:      break;
1436     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1437                            break;
1438     }
1439
1440     if (pPacket->SenseInfoLength != 0)
1441     {
1442         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1443                cmd.sense, pPacket->SenseInfoLength);
1444     }
1445
1446     pPacket->ScsiStatus = cmd.status;
1447
1448     ret = CDROM_GetStatusCode(io);
1449 #endif
1450     return ret;
1451 }
1452
1453 /******************************************************************
1454  *              CDROM_ScsiPassThrough
1455  *              Implements IOCTL_SCSI_PASS_THROUGH
1456  *
1457  */
1458 static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket)
1459 {
1460     int ret = STATUS_NOT_SUPPORTED;
1461 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1462     sg_io_hdr_t cmd;
1463     int io;
1464 #elif defined HAVE_SCSIREQ_T_CMD
1465     scsireq_t cmd;
1466     int io;
1467 #endif
1468
1469     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1470         return STATUS_BUFFER_TOO_SMALL;
1471
1472     if (pPacket->CdbLength > 16)
1473         return STATUS_INVALID_PARAMETER;
1474
1475 #ifdef SENSEBUFLEN
1476     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1477         return STATUS_INVALID_PARAMETER;
1478 #elif defined HAVE_REQUEST_SENSE
1479     if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1480         return STATUS_INVALID_PARAMETER;
1481 #endif
1482
1483     if (pPacket->DataTransferLength > 0 && pPacket->DataBufferOffset < sizeof(SCSI_PASS_THROUGH))
1484         return STATUS_INVALID_PARAMETER;
1485
1486 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1487     RtlZeroMemory(&cmd, sizeof(cmd));
1488
1489     cmd.interface_id   = 'S';
1490     cmd.dxfer_len      = pPacket->DataTransferLength;
1491     cmd.dxferp         = (char*)pPacket + pPacket->DataBufferOffset;
1492     cmd.cmd_len        = pPacket->CdbLength;
1493     cmd.cmdp           = pPacket->Cdb;
1494     cmd.mx_sb_len      = pPacket->SenseInfoLength;
1495     cmd.timeout        = pPacket->TimeOutValue*1000;
1496
1497     if(cmd.mx_sb_len > 0)
1498         cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1499
1500     switch (pPacket->DataIn)
1501     {
1502     case SCSI_IOCTL_DATA_IN:
1503         cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1504                              break;
1505     case SCSI_IOCTL_DATA_OUT:
1506         cmd.dxfer_direction = SG_DXFER_TO_DEV;
1507                              break;
1508     case SCSI_IOCTL_DATA_UNSPECIFIED:
1509         cmd.dxfer_direction = SG_DXFER_NONE;
1510                              break;
1511     default:
1512        return STATUS_INVALID_PARAMETER;
1513     }
1514
1515     io = ioctl(fd, SG_IO, &cmd);
1516
1517     pPacket->ScsiStatus         = cmd.status;
1518     pPacket->DataTransferLength = cmd.resid;
1519     pPacket->SenseInfoLength    = cmd.sb_len_wr;
1520
1521     ret = CDROM_GetStatusCode(io);
1522
1523 #elif defined HAVE_SCSIREQ_T_CMD
1524
1525     memset(&cmd, 0, sizeof(cmd));
1526     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1527
1528     if ( pPacket->DataBufferOffset > 0x1000 )
1529     {
1530         cmd.databuf     = (void*)pPacket->DataBufferOffset;
1531     }
1532     else
1533     {
1534         cmd.databuf     = (char*)pPacket + pPacket->DataBufferOffset;
1535     }
1536
1537     cmd.cmdlen         = pPacket->CdbLength;
1538     cmd.datalen        = pPacket->DataTransferLength;
1539     cmd.senselen       = pPacket->SenseInfoLength;
1540     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1541
1542     switch (pPacket->DataIn)
1543     {
1544     case SCSI_IOCTL_DATA_OUT:
1545         cmd.flags |= SCCMD_WRITE;
1546         break;
1547     case SCSI_IOCTL_DATA_IN:
1548         cmd.flags |= SCCMD_READ;
1549         break;
1550     case SCSI_IOCTL_DATA_UNSPECIFIED:
1551         cmd.flags = 0;
1552         break;
1553     default:
1554        return STATUS_INVALID_PARAMETER;
1555     }
1556
1557     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1558
1559     switch (cmd.retsts)
1560     {
1561     case SCCMD_OK:         break;
1562     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1563                            break;
1564     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1565                            break;
1566     case SCCMD_SENSE:      break;
1567     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1568                            break;
1569     }
1570
1571     if (pPacket->SenseInfoLength != 0)
1572     {
1573         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1574                cmd.sense, pPacket->SenseInfoLength);
1575     }
1576
1577     pPacket->ScsiStatus = cmd.status;
1578
1579     ret = CDROM_GetStatusCode(io);
1580 #endif
1581     return ret;
1582 }
1583
1584 /******************************************************************
1585  *              CDROM_ScsiGetCaps
1586  *
1587  *
1588  */
1589 static NTSTATUS CDROM_ScsiGetCaps(PIO_SCSI_CAPABILITIES caps)
1590 {
1591     NTSTATUS    ret = STATUS_NOT_IMPLEMENTED;
1592
1593     caps->Length = sizeof(*caps);
1594 #ifdef SG_SCATTER_SZ
1595     caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1596     caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1597     caps->SupportedAsynchronousEvents = TRUE;
1598     caps->AlignmentMask = getpagesize();
1599     caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1600     caps->AdapterScansDown = FALSE; /* FIXME ? */
1601     caps->AdapterUsesPio = FALSE; /* FIXME ? */
1602     ret = STATUS_SUCCESS;
1603 #else
1604     FIXME("Unimplemented\n");
1605 #endif
1606     return ret;
1607 }
1608
1609 /******************************************************************
1610  *              CDROM_GetAddress
1611  *
1612  * implements IOCTL_SCSI_GET_ADDRESS
1613  */
1614 static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address)
1615 {
1616     UCHAR portnum, busid, targetid, lun;
1617
1618     address->Length = sizeof(SCSI_ADDRESS);
1619     if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
1620         return STATUS_NOT_SUPPORTED;
1621
1622     address->PortNumber = portnum; /* primary=0 secondary=1 for ide */
1623     address->PathId = busid;       /* always 0 for ide */
1624     address->TargetId = targetid;  /* master=0 slave=1 for ide */
1625     address->Lun = lun;
1626     return STATUS_SUCCESS;
1627 }
1628
1629 /******************************************************************
1630  *              DVD_StartSession
1631  *
1632  *
1633  */
1634 static NTSTATUS DVD_StartSession(int fd, PDVD_SESSION_ID sid_in, PDVD_SESSION_ID sid_out)
1635 {
1636 #if defined(linux)
1637     NTSTATUS ret = STATUS_NOT_SUPPORTED;
1638     dvd_authinfo auth_info;
1639
1640     memset( &auth_info, 0, sizeof( auth_info ) );
1641     auth_info.type = DVD_LU_SEND_AGID;
1642     if (sid_in) auth_info.lsa.agid = *(int*)sid_in; /* ?*/
1643
1644     TRACE("fd 0x%08x\n",fd);
1645     ret =CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
1646     *sid_out = auth_info.lsa.agid;
1647     return ret;
1648 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1649     return STATUS_NOT_SUPPORTED;
1650 #else
1651     return STATUS_NOT_SUPPORTED;
1652 #endif
1653 }
1654
1655 /******************************************************************
1656  *              DVD_EndSession
1657  *
1658  *
1659  */
1660 static NTSTATUS DVD_EndSession(int fd, PDVD_SESSION_ID sid)
1661 {
1662 #if defined(linux)
1663     dvd_authinfo auth_info;
1664
1665     memset( &auth_info, 0, sizeof( auth_info ) );
1666     auth_info.type = DVD_INVALIDATE_AGID;
1667     auth_info.lsa.agid = *(int*)sid;
1668
1669     TRACE("\n");
1670     return CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
1671 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1672     return STATUS_NOT_SUPPORTED;
1673 #else
1674     return STATUS_NOT_SUPPORTED;
1675 #endif
1676 }
1677
1678 /******************************************************************
1679  *              DVD_SendKey
1680  *
1681  *
1682  */
1683 static NTSTATUS DVD_SendKey(int fd, PDVD_COPY_PROTECT_KEY key)
1684 {
1685 #if defined(linux)
1686     NTSTATUS ret = STATUS_NOT_SUPPORTED;
1687     dvd_authinfo auth_info;
1688
1689     memset( &auth_info, 0, sizeof( auth_info ) );
1690     switch (key->KeyType)
1691     {
1692     case DvdChallengeKey:
1693         auth_info.type = DVD_HOST_SEND_CHALLENGE;
1694         auth_info.hsc.agid = (int)key->SessionId;
1695         TRACE("DvdChallengeKey ioc 0x%x\n", DVD_AUTH );
1696         memcpy( auth_info.hsc.chal, key->KeyData, DVD_CHALLENGE_SIZE );
1697         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1698         break;
1699     case DvdBusKey2:
1700         auth_info.type = DVD_HOST_SEND_KEY2;
1701         auth_info.hsk.agid = (int)key->SessionId;
1702
1703         memcpy( auth_info.hsk.key, key->KeyData, DVD_KEY_SIZE );
1704         
1705         TRACE("DvdBusKey2\n");
1706         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1707         break;
1708
1709     default:
1710         FIXME("Unknown Keytype 0x%x\n",key->KeyType);
1711     }
1712     return ret;
1713 #else
1714     FIXME("unsupported on this platform\n");
1715     return STATUS_NOT_SUPPORTED;
1716 #endif    
1717 }
1718
1719 /******************************************************************
1720  *              DVD_ReadKey
1721  *
1722  *
1723  */
1724 static NTSTATUS DVD_ReadKey(int fd, PDVD_COPY_PROTECT_KEY key)
1725 {
1726 #if defined(linux)
1727     NTSTATUS ret = STATUS_NOT_SUPPORTED;
1728     dvd_struct dvd;
1729     dvd_authinfo auth_info;
1730
1731     memset( &dvd, 0, sizeof( dvd_struct ) );
1732     memset( &auth_info, 0, sizeof( auth_info ) );
1733     switch (key->KeyType)
1734     {
1735     case DvdDiskKey:
1736         
1737         dvd.type = DVD_STRUCT_DISCKEY;
1738         dvd.disckey.agid = (int)key->SessionId;
1739         memset( dvd.disckey.value, 0, DVD_DISCKEY_SIZE );
1740         
1741         TRACE("DvdDiskKey\n");
1742         ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd ));
1743         if (ret == STATUS_SUCCESS)
1744             memcpy(key->KeyData,dvd.disckey.value,DVD_DISCKEY_SIZE);
1745         break;
1746     case DvdTitleKey:
1747         auth_info.type = DVD_LU_SEND_TITLE_KEY;
1748         auth_info.lstk.agid = (int)key->SessionId;
1749         auth_info.lstk.lba = (int)(key->Parameters.TitleOffset.QuadPart>>11);
1750         TRACE("DvdTitleKey session %d Quadpart 0x%08lx offset 0x%08x\n",
1751               (int)key->SessionId, (long)key->Parameters.TitleOffset.QuadPart, 
1752               auth_info.lstk.lba);
1753         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1754         if (ret == STATUS_SUCCESS)
1755             memcpy(key->KeyData, auth_info.lstk.title_key, DVD_KEY_SIZE );
1756         break;
1757     case DvdChallengeKey:
1758
1759         auth_info.type = DVD_LU_SEND_CHALLENGE;
1760         auth_info.lsc.agid = (int)key->SessionId;
1761
1762         TRACE("DvdChallengeKey\n");
1763         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1764         if (ret == STATUS_SUCCESS)
1765             memcpy( key->KeyData, auth_info.lsc.chal, DVD_CHALLENGE_SIZE );
1766         break;
1767     case DvdAsf:
1768         auth_info.type = DVD_LU_SEND_ASF;
1769         TRACE("DvdAsf\n");
1770         auth_info.lsasf.asf=((PDVD_ASF)key->KeyData)->SuccessFlag;
1771         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1772         ((PDVD_ASF)key->KeyData)->SuccessFlag = auth_info.lsasf.asf;
1773         break;
1774     case DvdBusKey1:
1775         auth_info.type = DVD_LU_SEND_KEY1;
1776         auth_info.lsk.agid = (int)key->SessionId;
1777
1778         TRACE("DvdBusKey1\n");
1779         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1780
1781         if (ret == STATUS_SUCCESS)
1782             memcpy( key->KeyData, auth_info.lsk.key, DVD_KEY_SIZE );
1783         break;
1784     case DvdGetRpcKey:
1785         auth_info.type = DVD_LU_SEND_RPC_STATE;
1786
1787         TRACE("DvdGetRpcKey\n");
1788         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1789
1790         if (ret == STATUS_SUCCESS)
1791         {
1792             ((PDVD_RPC_KEY)key->KeyData)->TypeCode = auth_info.lrpcs.type;
1793             ((PDVD_RPC_KEY)key->KeyData)->RegionMask = auth_info.lrpcs.region_mask;
1794             ((PDVD_RPC_KEY)key->KeyData)->RpcScheme = auth_info.lrpcs.rpc_scheme;
1795         }
1796         break;
1797     default:
1798         FIXME("Unknown keytype 0x%x\n",key->KeyType);
1799     }
1800     return ret;
1801 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1802     TRACE("bsd\n");
1803     return STATUS_NOT_SUPPORTED;
1804 #else
1805     TRACE("outside\n");
1806     return STATUS_NOT_SUPPORTED;
1807 #endif
1808 }
1809
1810 /******************************************************************
1811  *              DVD_GetRegion
1812  *
1813  *
1814  */
1815 static NTSTATUS DVD_GetRegion(int dev, PDVD_REGION region)
1816 {
1817     FIXME("\n");
1818     return STATUS_SUCCESS;
1819
1820 }
1821
1822 /******************************************************************
1823  *              DVD_GetRegion
1824  *
1825  *
1826  */
1827 static NTSTATUS DVD_ReadStructure(int dev, PDVD_READ_STRUCTURE structure, PDVD_LAYER_DESCRIPTOR layer)
1828 {
1829 #ifdef DVD_READ_STRUCT
1830     dvd_struct s;
1831
1832     if (structure->BlockByteOffset.u.HighPart || structure->BlockByteOffset.u.LowPart)
1833         FIXME(": BlockByteOffset is not handled\n");
1834
1835     switch (structure->Format)
1836     {
1837     case DvdPhysicalDescriptor:
1838         s.type = DVD_STRUCT_PHYSICAL;
1839         s.physical.layer_num = structure->LayerNumber;
1840         break;
1841
1842     case DvdCopyrightDescriptor:
1843         s.type = DVD_STRUCT_COPYRIGHT;
1844         s.copyright.layer_num = structure->LayerNumber;
1845         break;
1846
1847     case DvdDiskKeyDescriptor:
1848         s.type = DVD_STRUCT_DISCKEY;
1849         s.disckey.agid = structure->SessionId;
1850         break;
1851
1852     case DvdBCADescriptor:
1853         s.type = DVD_STRUCT_BCA;
1854         break;
1855
1856     case DvdManufacturerDescriptor:
1857         s.type = DVD_STRUCT_MANUFACT;
1858         s.manufact.layer_num = structure->LayerNumber;
1859         break;
1860
1861     case DvdMaxDescriptor: /* This is not a real request, no matter what MSDN says! */
1862     default:
1863         return STATUS_INVALID_PARAMETER;
1864     }
1865
1866     if (ioctl(dev, DVD_READ_STRUCT, &s) < 0)
1867        return STATUS_INVALID_PARAMETER;
1868
1869     switch (structure->Format)
1870     {
1871     case DvdPhysicalDescriptor:
1872         {
1873             internal_dvd_layer_descriptor *p = (internal_dvd_layer_descriptor *) layer;
1874             struct dvd_layer *l = &s.physical.layer[s.physical.layer_num];
1875
1876             p->MagicHeader[0] = 2;
1877             p->MagicHeader[1] = 8;
1878             p->MagicHeader[2] = 0;
1879             p->MagicHeader[3] = 0;
1880             p->BookVersion = l->book_version;
1881             p->BookType = l->book_type;
1882             p->MinimumRate = l->min_rate;
1883             p->DiskSize = l->disc_size;
1884             p->LayerType = l->layer_type;
1885             p->TrackPath = l->track_path;
1886             p->NumberOfLayers = l->nlayers;
1887             p->Reserved1 = 0;
1888             p->TrackDensity = l->track_density;
1889             p->LinearDensity = l->linear_density;
1890             p->StartingDataSector = l->start_sector;
1891             p->EndDataSector = l->end_sector;
1892             p->EndLayerZeroSector = l->end_sector_l0;
1893             p->Reserved5 = 0;
1894             p->BCAFlag = l->bca;
1895             p->Reserved6 = 0;
1896         }
1897         break;
1898
1899     case DvdCopyrightDescriptor:
1900         {
1901             PDVD_COPYRIGHT_DESCRIPTOR p = (PDVD_COPYRIGHT_DESCRIPTOR) layer;
1902
1903             p->CopyrightProtectionType = s.copyright.cpst;
1904             p->RegionManagementInformation = s.copyright.rmi;
1905             p->Reserved = 0;
1906         }
1907         break;
1908
1909     case DvdDiskKeyDescriptor:
1910         {
1911             PDVD_DISK_KEY_DESCRIPTOR p = (PDVD_DISK_KEY_DESCRIPTOR) layer;
1912
1913             memcpy(p->DiskKeyData, s.disckey.value, 2048);
1914         }
1915         break;
1916
1917     case DvdBCADescriptor:
1918         {
1919             PDVD_BCA_DESCRIPTOR p = (PDVD_BCA_DESCRIPTOR) layer;
1920
1921             memcpy(p->BCAInformation, s.bca.value, s.bca.len);
1922         }
1923         break;
1924
1925     case DvdManufacturerDescriptor:
1926         {
1927             PDVD_MANUFACTURER_DESCRIPTOR p = (PDVD_MANUFACTURER_DESCRIPTOR) layer;
1928
1929             memcpy(p->ManufacturingInformation, s.manufact.value, 2048);
1930         }
1931         break;
1932
1933     case DvdMaxDescriptor: /* Suppress warning */
1934         break;
1935     }
1936 #else
1937     FIXME("\n");
1938 #endif
1939     return STATUS_SUCCESS;
1940
1941 }
1942
1943 /******************************************************************
1944  *        GetInquiryData
1945  *        Implements the IOCTL_GET_INQUIRY_DATA ioctl.
1946  *        Returns Inquiry data for all devices on the specified scsi bus
1947  *        Returns STATUS_BUFFER_TOO_SMALL if the output buffer is too small, 
1948  *        STATUS_INVALID_DEVICE_REQUEST if the given handle isn't to a SCSI device,
1949  *        or STATUS_NOT_SUPPORTED if the OS driver is too old
1950  */
1951 static NTSTATUS GetInquiryData(int fd, PSCSI_ADAPTER_BUS_INFO BufferOut, DWORD OutBufferSize)
1952 {
1953 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1954     PSCSI_INQUIRY_DATA pInquiryData = NULL;
1955     UCHAR sense_buffer[32];
1956     int iochk, version;
1957     sg_io_hdr_t iocmd;
1958     UCHAR inquiry[INQ_CMD_LEN] = {INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0};
1959
1960     /* Check we have a SCSI device and a supported driver */
1961     if(ioctl(fd, SG_GET_VERSION_NUM, &version) != 0)
1962     {
1963         WARN("IOCTL_SCSI_GET_INQUIRY_DATA sg driver is not loaded\n");
1964         return STATUS_INVALID_DEVICE_REQUEST;
1965     }
1966     if(version < 30000 )
1967         return STATUS_NOT_SUPPORTED;
1968
1969     /* FIXME: Enumerate devices on the bus */
1970     BufferOut->NumberOfBuses = 1;
1971     BufferOut->BusData[0].NumberOfLogicalUnits = 1;
1972     BufferOut->BusData[0].InquiryDataOffset = sizeof(SCSI_ADAPTER_BUS_INFO);
1973
1974     pInquiryData = (PSCSI_INQUIRY_DATA)(BufferOut + 1);
1975
1976     RtlZeroMemory(&iocmd, sizeof(iocmd));
1977     iocmd.interface_id = 'S';
1978     iocmd.cmd_len = sizeof(inquiry);
1979     iocmd.mx_sb_len = sizeof(sense_buffer);
1980     iocmd.dxfer_direction = SG_DXFER_FROM_DEV;
1981     iocmd.dxfer_len = INQ_REPLY_LEN;
1982     iocmd.dxferp = pInquiryData->InquiryData;
1983     iocmd.cmdp = inquiry;
1984     iocmd.sbp = sense_buffer;
1985     iocmd.timeout = 1000;
1986
1987     iochk = ioctl(fd, SG_IO, &iocmd);
1988     if(iochk != 0)
1989         WARN("ioctl SG_IO returned %d, error (%s)\n", iochk, strerror(errno));
1990
1991     CDROM_GetInterfaceInfo(fd, &BufferOut->BusData[0].InitiatorBusId, &pInquiryData->PathId, &pInquiryData->TargetId, &pInquiryData->Lun);
1992     pInquiryData->DeviceClaimed = TRUE;
1993     pInquiryData->InquiryDataLength = INQ_REPLY_LEN;
1994     pInquiryData->NextInquiryDataOffset = 0;
1995     return STATUS_SUCCESS;
1996 #else
1997     FIXME("not implemented for nonlinux\n");
1998     return STATUS_NOT_SUPPORTED;
1999 #endif
2000 }
2001
2002 /******************************************************************
2003  *              CDROM_DeviceIoControl
2004  *
2005  *
2006  */
2007 NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, 
2008                                HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
2009                                PVOID UserApcContext, 
2010                                PIO_STATUS_BLOCK piosb, 
2011                                ULONG dwIoControlCode,
2012                                LPVOID lpInBuffer, DWORD nInBufferSize,
2013                                LPVOID lpOutBuffer, DWORD nOutBufferSize)
2014 {
2015     DWORD       sz = 0;
2016     NTSTATUS    status = STATUS_SUCCESS;
2017     int fd, dev;
2018
2019     TRACE("%p %s %p %d %p %d %p\n",
2020           hDevice, iocodex(dwIoControlCode), lpInBuffer, nInBufferSize,
2021           lpOutBuffer, nOutBufferSize, piosb);
2022
2023     piosb->Information = 0;
2024
2025     if ((status = wine_server_handle_to_fd( hDevice, 0, &fd, NULL ))) goto error;
2026     if ((status = CDROM_Open(fd, &dev)))
2027     {
2028         wine_server_release_fd( hDevice, fd );
2029         goto error;
2030     }
2031
2032     switch (dwIoControlCode)
2033     {
2034     case IOCTL_STORAGE_CHECK_VERIFY:
2035     case IOCTL_CDROM_CHECK_VERIFY:
2036     case IOCTL_DISK_CHECK_VERIFY:
2037         sz = 0;
2038         CDROM_ClearCacheEntry(dev);
2039         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2040             status = STATUS_INVALID_PARAMETER;
2041         else status = CDROM_Verify(dev, fd);
2042         break;
2043
2044 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
2045
2046 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
2047 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
2048
2049     case IOCTL_STORAGE_LOAD_MEDIA:
2050     case IOCTL_CDROM_LOAD_MEDIA:
2051         sz = 0;
2052         CDROM_ClearCacheEntry(dev);
2053         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2054             status = STATUS_INVALID_PARAMETER;
2055         else status = CDROM_SetTray(fd, FALSE);
2056         break;
2057      case IOCTL_STORAGE_EJECT_MEDIA:
2058         sz = 0;
2059         CDROM_ClearCacheEntry(dev);
2060         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2061             status = STATUS_INVALID_PARAMETER;
2062         else status = CDROM_SetTray(fd, TRUE);
2063         break;
2064
2065     case IOCTL_CDROM_MEDIA_REMOVAL:
2066     case IOCTL_DISK_MEDIA_REMOVAL:
2067     case IOCTL_STORAGE_MEDIA_REMOVAL:
2068     case IOCTL_STORAGE_EJECTION_CONTROL:
2069         /* FIXME the last ioctl:s is not the same as the two others...
2070          * lockcount/owner should be handled */
2071         sz = 0;
2072         CDROM_ClearCacheEntry(dev);
2073         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2074         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL;
2075         else status = CDROM_ControlEjection(fd, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
2076         break;
2077
2078 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
2079
2080     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
2081         sz = sizeof(STORAGE_DEVICE_NUMBER);
2082         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2083         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2084         else status = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
2085         break;
2086
2087     case IOCTL_STORAGE_RESET_DEVICE:
2088         sz = 0;
2089         CDROM_ClearCacheEntry(dev);
2090         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2091             status = STATUS_INVALID_PARAMETER;
2092         else status = CDROM_ResetAudio(fd);
2093         break;
2094
2095     case IOCTL_CDROM_GET_CONTROL:
2096         sz = sizeof(CDROM_AUDIO_CONTROL);
2097         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2098         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2099         else status = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
2100         break;
2101
2102     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
2103         sz = sizeof(DISK_GEOMETRY);
2104         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2105         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2106         else status = CDROM_GetDriveGeometry(dev, fd, (DISK_GEOMETRY*)lpOutBuffer);
2107         break;
2108
2109     case IOCTL_CDROM_DISK_TYPE:
2110         sz = sizeof(CDROM_DISK_DATA);
2111         /* CDROM_ClearCacheEntry(dev); */
2112         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2113         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2114         else status = CDROM_GetDiskData(dev, fd, (CDROM_DISK_DATA*)lpOutBuffer);
2115         break;
2116
2117 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
2118
2119     case IOCTL_CDROM_READ_Q_CHANNEL:
2120         sz = sizeof(SUB_Q_CHANNEL_DATA);
2121         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
2122             status = STATUS_INVALID_PARAMETER;
2123         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2124         else status = CDROM_ReadQChannel(dev, fd, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
2125                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
2126         break;
2127
2128     case IOCTL_CDROM_READ_TOC:
2129         sz = sizeof(CDROM_TOC);
2130         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2131         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2132         else status = CDROM_ReadTOC(dev, fd, (CDROM_TOC*)lpOutBuffer);
2133         break;
2134
2135 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
2136
2137     case IOCTL_CDROM_PAUSE_AUDIO:
2138         sz = 0;
2139         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2140             status = STATUS_INVALID_PARAMETER;
2141         else status = CDROM_PauseAudio(fd);
2142         break;
2143     case IOCTL_CDROM_PLAY_AUDIO_MSF:
2144         sz = 0;
2145         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2146         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
2147         else status = CDROM_PlayAudioMSF(fd, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
2148         break;
2149     case IOCTL_CDROM_RESUME_AUDIO:
2150         sz = 0;
2151         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2152             status = STATUS_INVALID_PARAMETER;
2153         else status = CDROM_ResumeAudio(fd);
2154         break;
2155     case IOCTL_CDROM_SEEK_AUDIO_MSF:
2156         sz = 0;
2157         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2158         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
2159         else status = CDROM_SeekAudioMSF(dev, fd, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
2160         break;
2161     case IOCTL_CDROM_STOP_AUDIO:
2162         sz = 0;
2163         CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
2164         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2165             status = STATUS_INVALID_PARAMETER;
2166         else status = CDROM_StopAudio(fd);
2167         break;
2168     case IOCTL_CDROM_GET_VOLUME:
2169         sz = sizeof(VOLUME_CONTROL);
2170         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2171         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2172         else status = CDROM_GetVolume(fd, (VOLUME_CONTROL*)lpOutBuffer);
2173         break;
2174     case IOCTL_CDROM_SET_VOLUME:
2175         sz = 0;
2176         CDROM_ClearCacheEntry(dev);
2177         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
2178             status = STATUS_INVALID_PARAMETER;
2179         else status = CDROM_SetVolume(fd, (const VOLUME_CONTROL*)lpInBuffer);
2180         break;
2181     case IOCTL_CDROM_RAW_READ:
2182         sz = 0;
2183         if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER;
2184         else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL;
2185         else status = CDROM_RawRead(fd, (const RAW_READ_INFO*)lpInBuffer,
2186                                    lpOutBuffer, nOutBufferSize, &sz);
2187         break;
2188     case IOCTL_SCSI_GET_ADDRESS:
2189         sz = sizeof(SCSI_ADDRESS);
2190         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2191         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2192         else status = CDROM_GetAddress(fd, (SCSI_ADDRESS*)lpOutBuffer);
2193         break;
2194     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
2195         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
2196         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2197         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL;
2198         else status = CDROM_ScsiPassThroughDirect(fd, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
2199         break;
2200     case IOCTL_SCSI_PASS_THROUGH:
2201         sz = sizeof(SCSI_PASS_THROUGH);
2202         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2203         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL;
2204         else status = CDROM_ScsiPassThrough(fd, (PSCSI_PASS_THROUGH)lpOutBuffer);
2205         break;
2206     case IOCTL_SCSI_GET_CAPABILITIES:
2207         sz = sizeof(IO_SCSI_CAPABILITIES);
2208         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2209         else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL;
2210         else status = CDROM_ScsiGetCaps((PIO_SCSI_CAPABILITIES)lpOutBuffer);
2211         break;
2212     case IOCTL_DVD_START_SESSION:
2213         sz = sizeof(DVD_SESSION_ID);
2214         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2215         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2216         else
2217         {
2218             TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
2219                   *(PDVD_SESSION_ID)lpOutBuffer);
2220             status = DVD_StartSession(fd, (PDVD_SESSION_ID)lpInBuffer, (PDVD_SESSION_ID)lpOutBuffer);
2221             TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
2222                   *(PDVD_SESSION_ID)lpOutBuffer);
2223         }
2224         break;
2225     case IOCTL_DVD_END_SESSION:
2226         sz = sizeof(DVD_SESSION_ID);
2227         if ((lpInBuffer == NULL) ||  (nInBufferSize < sz))status = STATUS_INVALID_PARAMETER;
2228         else status = DVD_EndSession(fd, (PDVD_SESSION_ID)lpInBuffer);
2229         break;
2230     case IOCTL_DVD_SEND_KEY:
2231         sz = 0;
2232         if (!lpInBuffer ||
2233             (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
2234             status = STATUS_INVALID_PARAMETER;
2235         else
2236         {
2237             TRACE("doing DVD_SendKey\n");
2238             status = DVD_SendKey(fd, (PDVD_COPY_PROTECT_KEY)lpInBuffer);
2239         }
2240         break;
2241     case IOCTL_DVD_READ_KEY:
2242         if (!lpInBuffer ||
2243             (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
2244             status = STATUS_INVALID_PARAMETER;
2245         else if (lpInBuffer !=lpOutBuffer) status = STATUS_BUFFER_TOO_SMALL;
2246         else
2247         {
2248             TRACE("doing DVD_READ_KEY\n");
2249             sz = ((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength;
2250             status = DVD_ReadKey(fd, (PDVD_COPY_PROTECT_KEY)lpInBuffer);
2251         }
2252         break;
2253     case IOCTL_DVD_GET_REGION:
2254         sz = sizeof(DVD_REGION);
2255         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2256         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2257         TRACE("doing DVD_Get_REGION\n");
2258         status = DVD_GetRegion(fd, (PDVD_REGION)lpOutBuffer);
2259         break;
2260     case IOCTL_DVD_READ_STRUCTURE:
2261         sz = sizeof(DVD_LAYER_DESCRIPTOR);
2262         if (lpInBuffer == NULL || nInBufferSize != sizeof(DVD_READ_STRUCTURE)) status = STATUS_INVALID_PARAMETER;
2263         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2264         else
2265         {
2266             TRACE("doing DVD_READ_STRUCTURE\n");
2267             status = DVD_ReadStructure(fd, (PDVD_READ_STRUCTURE)lpInBuffer, (PDVD_LAYER_DESCRIPTOR)lpOutBuffer);
2268         }
2269         break;
2270
2271     case IOCTL_SCSI_GET_INQUIRY_DATA:
2272         sz = INQ_REPLY_LEN;
2273         status = GetInquiryData(fd, (PSCSI_ADAPTER_BUS_INFO)lpOutBuffer, nOutBufferSize);
2274         break;
2275
2276     default:
2277         FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n", 
2278               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
2279               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
2280         sz = 0;
2281         status = STATUS_INVALID_PARAMETER;
2282         break;
2283     }
2284     wine_server_release_fd( hDevice, fd );
2285  error:
2286     piosb->u.Status = status;
2287     piosb->Information = sz;
2288     if (hEvent) NtSetEvent(hEvent, NULL);
2289     return status;
2290 }