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