ntdll: Add a __chkstk entry point for x86_64.
[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 # undef FAILED           /* avoid conflict with winerror.h */
53 #endif
54 #ifdef HAVE_SCSI_SCSI_IOCTL_H
55 # include <scsi/scsi_ioctl.h>
56 #endif
57 #ifdef HAVE_LINUX_MAJOR_H
58 # include <linux/major.h>
59 #endif
60 #ifdef HAVE_LINUX_HDREG_H
61 # include <linux/hdreg.h>
62 #endif
63 #ifdef HAVE_LINUX_PARAM_H
64 # include <linux/param.h>
65 #endif
66 #ifdef HAVE_LINUX_CDROM_H
67 # include <linux/cdrom.h>
68 #endif
69 #ifdef HAVE_LINUX_UCDROM_H
70 # include <linux/ucdrom.h>
71 #endif
72 #ifdef HAVE_SYS_CDIO_H
73 # include <sys/cdio.h>
74 #endif
75 #ifdef HAVE_SYS_SCSIIO_H
76 # include <sys/scsiio.h>
77 #endif
78
79 #ifdef HAVE_IOKIT_IOKITLIB_H
80 # include <libkern/OSByteOrder.h>
81 # include <sys/disk.h>
82 # include <IOKit/IOKitLib.h>
83 # include <IOKit/storage/IOMedia.h>
84 # include <IOKit/storage/IOCDMediaBSDClient.h>
85 # include <IOKit/storage/IODVDMediaBSDClient.h>
86 # include <IOKit/scsi/SCSITask.h>
87 # include <IOKit/scsi/SCSICmds_REQUEST_SENSE_Defs.h>
88 # define SENSEBUFLEN kSenseDefaultSize
89
90 typedef struct
91 {
92     uint32_t attribute;
93     uint32_t timeout;
94     uint32_t response;
95     uint32_t status;
96     uint8_t direction;
97     uint8_t cdbSize;
98     uint8_t reserved0144[2];
99     uint8_t cdb[16];
100     void* buffer;
101     uint64_t bufferSize;
102     void* sense;
103     uint64_t senseLen;
104 } dk_scsi_command_t;
105
106 typedef struct
107 {
108     uint64_t bus;
109     uint64_t port;
110     uint64_t target;
111     uint64_t lun;
112 } dk_scsi_identify_t;
113
114 #define DKIOCSCSICOMMAND _IOWR('d', 253, dk_scsi_command_t)
115 #define DKIOCSCSIIDENTIFY _IOR('d', 254, dk_scsi_identify_t)
116
117 #endif
118
119 #define NONAMELESSUNION
120 #define NONAMELESSSTRUCT
121 #include "ntstatus.h"
122 #define WIN32_NO_STATUS
123 #include "windef.h"
124 #include "winternl.h"
125 #include "winioctl.h"
126 #include "ntddstor.h"
127 #include "ntddcdrm.h"
128 #include "ddk/ntddcdvd.h"
129 #include "ntddscsi.h"
130 #include "ntdll_misc.h"
131 #include "wine/server.h"
132 #include "wine/library.h"
133 #include "wine/debug.h"
134
135 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
136
137 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
138    lack the following constants. */
139
140 #ifndef CD_SECS
141 # define CD_SECS              60 /* seconds per minute */
142 #endif
143 #ifndef CD_FRAMES
144 # define CD_FRAMES            75 /* frames per second */
145 #endif
146
147 #ifdef WORDS_BIGENDIAN
148 #define GET_BE_DWORD(x) (x)
149 #else
150 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
151 #endif
152
153 static const struct iocodexs
154 {
155   DWORD code;
156   const char *codex;
157 } iocodextable[] = {
158 #define X(x) { x, #x },
159 X(IOCTL_CDROM_CHECK_VERIFY)
160 X(IOCTL_CDROM_CURRENT_POSITION)
161 X(IOCTL_CDROM_DISK_TYPE)
162 X(IOCTL_CDROM_GET_CONTROL)
163 X(IOCTL_CDROM_GET_DRIVE_GEOMETRY)
164 X(IOCTL_CDROM_GET_VOLUME)
165 X(IOCTL_CDROM_LOAD_MEDIA)
166 X(IOCTL_CDROM_MEDIA_CATALOG)
167 X(IOCTL_CDROM_MEDIA_REMOVAL)
168 X(IOCTL_CDROM_PAUSE_AUDIO)
169 X(IOCTL_CDROM_PLAY_AUDIO_MSF)
170 X(IOCTL_CDROM_RAW_READ)
171 X(IOCTL_CDROM_READ_Q_CHANNEL)
172 X(IOCTL_CDROM_READ_TOC)
173 X(IOCTL_CDROM_RESUME_AUDIO)
174 X(IOCTL_CDROM_SEEK_AUDIO_MSF)
175 X(IOCTL_CDROM_SET_VOLUME)
176 X(IOCTL_CDROM_STOP_AUDIO)
177 X(IOCTL_CDROM_TRACK_ISRC)
178 X(IOCTL_DISK_MEDIA_REMOVAL)
179 X(IOCTL_DVD_END_SESSION)
180 X(IOCTL_DVD_GET_REGION)
181 X(IOCTL_DVD_READ_KEY)
182 X(IOCTL_DVD_READ_STRUCTURE)
183 X(IOCTL_DVD_SEND_KEY)
184 X(IOCTL_DVD_START_SESSION)
185 X(IOCTL_SCSI_GET_ADDRESS)
186 X(IOCTL_SCSI_GET_CAPABILITIES)
187 X(IOCTL_SCSI_GET_INQUIRY_DATA)
188 X(IOCTL_SCSI_PASS_THROUGH)
189 X(IOCTL_SCSI_PASS_THROUGH_DIRECT)
190 X(IOCTL_STORAGE_CHECK_VERIFY)
191 X(IOCTL_STORAGE_CHECK_VERIFY2)
192 X(IOCTL_STORAGE_EJECTION_CONTROL)
193 X(IOCTL_STORAGE_EJECT_MEDIA)
194 X(IOCTL_STORAGE_GET_DEVICE_NUMBER)
195 X(IOCTL_STORAGE_GET_MEDIA_TYPES)
196 X(IOCTL_STORAGE_GET_MEDIA_TYPES_EX)
197 X(IOCTL_STORAGE_LOAD_MEDIA)
198 X(IOCTL_STORAGE_MEDIA_REMOVAL)
199 X(IOCTL_STORAGE_RESET_DEVICE)
200 #undef X
201 };
202 static const char *iocodex(DWORD code)
203 {
204    unsigned int i;
205    static char buffer[25];
206    for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
207       if (code==iocodextable[i].code)
208          return iocodextable[i].codex;
209    sprintf(buffer, "IOCTL_CODE_%x", (int)code);
210    return buffer;
211 }
212
213 #define INQ_REPLY_LEN 36
214 #define INQ_CMD_LEN 6
215
216 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
217 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
218 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
219 #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;}
220
221 /* The documented format of DVD_LAYER_DESCRIPTOR is wrong. Even the format in the
222  * DDK's header is wrong. There are four bytes at the start  defined by
223  * MMC-5. The first two are the size of the structure in big-endian order as
224  * defined by MMC-5. The other two are reserved.
225  */
226 typedef struct
227 {
228     UCHAR DataLength[2];
229     UCHAR Reserved0[2];
230     UCHAR BookVersion : 4;
231     UCHAR BookType : 4;
232     UCHAR MinimumRate : 4;
233     UCHAR DiskSize : 4;
234     UCHAR LayerType : 4;
235     UCHAR TrackPath : 1;
236     UCHAR NumberOfLayers : 2;
237     UCHAR Reserved1 : 1;
238     UCHAR TrackDensity : 4;
239     UCHAR LinearDensity : 4;
240     ULONG StartingDataSector;
241     ULONG EndDataSector;
242     ULONG EndLayerZeroSector;
243     UCHAR Reserved5 : 7;
244     UCHAR BCAFlag : 1;
245     UCHAR Reserved6;
246 } internal_dvd_layer_descriptor;
247
248
249 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
250 static NTSTATUS CDROM_GetStatusCode(int);
251
252
253 #ifdef linux
254
255 # ifndef IDE6_MAJOR
256 #  define IDE6_MAJOR 88
257 # endif
258 # ifndef IDE7_MAJOR
259 #  define IDE7_MAJOR 89
260 # endif
261
262 # ifdef CDROM_SEND_PACKET
263 /* structure for CDROM_PACKET_COMMAND ioctl */
264 /* not all Linux versions have all the fields, so we define the
265  * structure ourselves to make sure */
266 struct linux_cdrom_generic_command
267 {
268     unsigned char          cmd[CDROM_PACKET_SIZE];
269     unsigned char         *buffer;
270     unsigned int           buflen;
271     int                    stat;
272     struct request_sense  *sense;
273     unsigned char          data_direction;
274     int                    quiet;
275     int                    timeout;
276     void                  *reserved[1];
277 };
278 # endif  /* CDROM_SEND_PACKET */
279
280 #endif  /* linux */
281
282 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
283  * this should be removed when a proper device interface is implemented
284  * 
285  * (WS) We need this to keep track of current position and to safely
286  * detect media changes. Besides this should provide a great speed up
287  * for toc inquiries.
288  */
289 struct cdrom_cache {
290     dev_t       device;
291     ino_t       inode;
292     char        toc_good; /* if false, will reread TOC from disk */
293     CDROM_TOC   toc;
294     SUB_Q_CURRENT_POSITION CurrentPosition;
295 };
296 /* who has more than 5 cdroms on his/her machine ?? */
297 /* FIXME: this should grow depending on the number of cdroms we install/configure 
298  * at startup
299  */
300 #define MAX_CACHE_ENTRIES       5
301 static struct cdrom_cache cdrom_cache[MAX_CACHE_ENTRIES];
302
303 static RTL_CRITICAL_SECTION cache_section;
304 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
305 {
306     0, 0, &cache_section,
307     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
308       0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
309 };
310 static RTL_CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
311
312 /* Proposed media change function: not really needed at this time */
313 /* This is a 1 or 0 type of function */
314 #if 0
315 static int CDROM_MediaChanged(int dev)
316 {
317    int i;
318
319    struct cdrom_tochdr  hdr;
320    struct cdrom_tocentry entry;
321
322    if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
323       return 0;
324    if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
325       return 0;
326
327    if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
328       return 1;
329
330    for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
331    {
332       if (i == hdr.cdth_trk1 + 1)
333       {
334          entry.cdte_track = CDROM_LEADOUT;
335       } else {
336          entry.cdte_track = i;
337       }
338       entry.cdte_format = CDROM_MSF;
339       if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
340          return 0;
341       if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
342                               sizeof(struct cdrom_tocentry)) )
343          return 1;
344    }
345    return 0;
346 }
347 #endif
348
349
350 /******************************************************************
351  *              get_parent_device
352  *
353  * On Mac OS, get the device for the whole disk from a fd that points to a partition.
354  * This is ugly and inefficient, but we have no choice since the partition fd doesn't
355  * support the eject ioctl.
356  */
357 #ifdef __APPLE__
358 static NTSTATUS get_parent_device( int fd, char *name, size_t len )
359 {
360     NTSTATUS status = STATUS_NO_SUCH_FILE;
361     struct stat st;
362     int i;
363     io_service_t service;
364     CFMutableDictionaryRef dict;
365     CFTypeRef val;
366
367     if (fstat( fd, &st ) == -1) return FILE_GetNtStatus();
368     if (!S_ISCHR( st.st_mode )) return STATUS_OBJECT_TYPE_MISMATCH;
369
370     /* create a dictionary with the right major/minor numbers */
371
372     if (!(dict = IOServiceMatching( kIOMediaClass ))) return STATUS_NO_MEMORY;
373
374     i = major( st.st_rdev );
375     val = CFNumberCreate( NULL, kCFNumberIntType, &i );
376     CFDictionaryAddValue( dict, CFSTR( "BSD Major" ), val );
377     CFRelease( val );
378
379     i = minor( st.st_rdev );
380     val = CFNumberCreate( NULL, kCFNumberIntType, &i );
381     CFDictionaryAddValue( dict, CFSTR( "BSD Minor" ), val );
382     CFRelease( val );
383
384     CFDictionaryAddValue( dict, CFSTR("Removable"), kCFBooleanTrue );
385
386     service = IOServiceGetMatchingService( kIOMasterPortDefault, dict );
387
388     /* now look for the parent that has the "Whole" attribute set to TRUE */
389
390     while (service)
391     {
392         io_service_t parent = 0;
393         CFBooleanRef whole;
394         CFStringRef str;
395         int ok;
396
397         if (!IOObjectConformsTo( service, kIOMediaClass ))
398             goto next;
399         if (!(whole = IORegistryEntryCreateCFProperty( service, CFSTR("Whole"), NULL, 0 )))
400             goto next;
401         ok = (whole == kCFBooleanTrue);
402         CFRelease( whole );
403         if (!ok) goto next;
404
405         if ((str = IORegistryEntryCreateCFProperty( service, CFSTR("BSD Name"), NULL, 0 )))
406         {
407             strcpy( name, "/dev/r" );
408             CFStringGetCString( str, name + 6, len - 6, kCFStringEncodingUTF8 );
409             CFRelease( str );
410             status = STATUS_SUCCESS;
411         }
412         IOObjectRelease( service );
413         break;
414
415 next:
416         IORegistryEntryGetParentEntry( service, kIOServicePlane, &parent );
417         IOObjectRelease( service );
418         service = parent;
419     }
420     return status;
421 }
422 #endif
423
424
425 /******************************************************************
426  *              CDROM_SyncCache                          [internal]
427  *
428  * Read the TOC in and store it in the cdrom_cache structure.
429  * Further requests for the TOC will be copied from the cache
430  * unless certain events like disk ejection is detected, in which
431  * case the cache will be cleared, causing it to be resynced.
432  * The cache section must be held by caller.
433  */
434 static NTSTATUS CDROM_SyncCache(int dev, int fd)
435 {
436 #ifdef linux
437    int i, tsz;
438    struct cdrom_tochdr          hdr;
439    struct cdrom_tocentry        entry;
440
441    CDROM_TOC *toc = &cdrom_cache[dev].toc;
442    cdrom_cache[dev].toc_good = 0;
443
444    if (ioctl(fd, CDROMREADTOCHDR, &hdr) == -1)
445    {
446       WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
447       return FILE_GetNtStatus();
448    }
449
450    toc->FirstTrack = hdr.cdth_trk0;
451    toc->LastTrack  = hdr.cdth_trk1;
452    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
453        + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
454    toc->Length[0] = tsz >> 8;
455    toc->Length[1] = tsz;
456
457    TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
458
459    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
460    {
461      if (i == toc->LastTrack + 1)
462        entry.cdte_track = CDROM_LEADOUT;
463      else 
464        entry.cdte_track = i;
465      entry.cdte_format = CDROM_MSF;
466      if (ioctl(fd, CDROMREADTOCENTRY, &entry) == -1)
467      {
468        WARN("error read entry (%s)\n", strerror(errno));
469        return FILE_GetNtStatus();
470      }
471      toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
472      toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
473      /* marking last track with leadout value as index */
474      toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
475      toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
476      toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
477      toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
478      toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
479    }
480    cdrom_cache[dev].toc_good = 1;
481    return STATUS_SUCCESS;
482
483 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
484
485    int i, tsz;
486    struct ioc_toc_header hdr;
487    struct ioc_read_toc_entry entry;
488    struct cd_toc_entry toc_buffer;
489
490    CDROM_TOC *toc = &cdrom_cache[dev].toc;
491    cdrom_cache[dev].toc_good = 0;
492
493     if (ioctl(fd, CDIOREADTOCHEADER, &hdr) == -1)
494     {
495         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
496         return FILE_GetNtStatus();
497     }
498     toc->FirstTrack = hdr.starting_track;
499     toc->LastTrack  = hdr.ending_track;
500     tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
501         + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
502     toc->Length[0] = tsz >> 8;
503     toc->Length[1] = tsz;
504
505     TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
506
507     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
508     {
509         if (i == toc->LastTrack + 1)
510         {
511 #define LEADOUT 0xaa
512             entry.starting_track = LEADOUT;
513         } else {
514             entry.starting_track = i;
515         }
516         memset(&toc_buffer, 0, sizeof(toc_buffer));
517         entry.address_format = CD_MSF_FORMAT;
518         entry.data_len = sizeof(toc_buffer);
519         entry.data = &toc_buffer;
520         if (ioctl(fd, CDIOREADTOCENTRYS, &entry) == -1)
521         {
522             WARN("error read entry (%s)\n", strerror(errno));
523             return FILE_GetNtStatus();
524         }
525         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
526         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
527         /* marking last track with leadout value as index */
528         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
529         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
530         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
531         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
532         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
533     }
534     cdrom_cache[dev].toc_good = 1;
535     return STATUS_SUCCESS;
536
537 #elif defined(__APPLE__)
538     int i;
539     dk_cd_read_toc_t hdr;
540     CDROM_TOC *toc = &cdrom_cache[dev].toc;
541     cdrom_cache[dev].toc_good = 0;
542
543     memset( &hdr, 0, sizeof(hdr) );
544     hdr.buffer = toc;
545     hdr.bufferLength = sizeof(*toc);
546     if (ioctl(fd, DKIOCCDREADTOC, &hdr) == -1)
547     {
548         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
549         return FILE_GetNtStatus();
550     }
551     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
552     {
553         /* convert address format */
554         TRACK_DATA *data = &toc->TrackData[i - toc->FirstTrack];
555         DWORD frame = (((DWORD)data->Address[0] << 24) | ((DWORD)data->Address[1] << 16) |
556                        ((DWORD)data->Address[2] << 8) | data->Address[3]);
557         MSF_OF_FRAME( data->Address[1], frame );
558         data->Address[0] = 0;
559     }
560
561     cdrom_cache[dev].toc_good = 1;
562     return STATUS_SUCCESS;
563 #else
564     FIXME("not supported on this O/S\n");
565     return STATUS_NOT_SUPPORTED;
566 #endif
567 }
568
569 static void CDROM_ClearCacheEntry(int dev)
570 {
571     RtlEnterCriticalSection( &cache_section );
572     cdrom_cache[dev].toc_good = 0;
573     RtlLeaveCriticalSection( &cache_section );
574 }
575
576
577
578 /******************************************************************
579  *              CDROM_GetInterfaceInfo
580  *
581  * Determines the ide interface (the number after the ide), and the
582  * number of the device on that interface for ide cdroms (*iface <= 1).
583  * Determines the scsi information for scsi cdroms (*iface >= 2).
584  * Returns false if the info cannot not be obtained.
585  */
586 static int CDROM_GetInterfaceInfo(int fd, UCHAR* iface, UCHAR* port, UCHAR* device, UCHAR* lun)
587 {
588 #if defined(linux)
589     struct stat st;
590     if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) return 0;
591     *port = 0;
592     *iface = 0;
593     *device = 0;
594     *lun = 0;
595     switch (major(st.st_rdev)) {
596     case IDE0_MAJOR: *iface = 0; break;
597     case IDE1_MAJOR: *iface = 1; break;
598     case IDE2_MAJOR: *iface = 2; break;
599     case IDE3_MAJOR: *iface = 3; break;
600     case IDE4_MAJOR: *iface = 4; break;
601     case IDE5_MAJOR: *iface = 5; break;
602     case IDE6_MAJOR: *iface = 6; break;
603     case IDE7_MAJOR: *iface = 7; break;
604     default: *port = 1; break;
605     }
606
607     if (*port == 0)
608         *device = (minor(st.st_rdev) >> 6);
609     else
610     {
611 #ifdef SCSI_IOCTL_GET_IDLUN
612         UINT32 idlun[2];
613         if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, idlun) != -1)
614         {
615             *port = (idlun[0] >> 24) & 0xff;
616             *iface = ((idlun[0] >> 16) & 0xff) + 2;
617             *device = idlun[0] & 0xff;
618             *lun = (idlun[0] >> 8) & 0xff;
619         }
620         else
621 #endif
622         {
623             WARN("CD-ROM device (%d, %d) not supported\n", major(st.st_rdev), minor(st.st_rdev));
624             return 0;
625         }
626     }
627     return 1;
628 #elif defined(__NetBSD__)
629     struct scsi_addr addr;
630     if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1)
631     {
632         switch (addr.type)
633         {
634         case TYPE_SCSI:
635             *port = 1;
636             *iface = addr.addr.scsi.scbus;
637             *device = addr.addr.scsi.target;
638             *lun = addr.addr.scsi.lun;
639             return 1;
640         case TYPE_ATAPI:
641             *port = 0;
642             *iface = addr.addr.atapi.atbus;
643             *device = addr.addr.atapi.drive;
644             *lun = 0;
645             return 1;
646         }
647     }
648     return 0;
649 #elif defined(__APPLE__)
650     dk_scsi_identify_t addr;
651     if (ioctl(fd, DKIOCSCSIIDENTIFY, &addr) != -1)
652     {
653        *port = addr.bus;
654        *iface = addr.port;
655        *device = addr.target;
656        *lun = addr.lun;
657        return 1;
658     }
659     return 0;
660 #else
661     FIXME("not implemented on this O/S\n");
662     return 0;
663 #endif
664 }
665
666
667 /******************************************************************
668  *              CDROM_Open
669  *
670  */
671 static NTSTATUS CDROM_Open(int fd, int* dev)
672 {
673     struct stat st;
674     NTSTATUS ret = STATUS_SUCCESS;
675     int         empty = -1;
676
677     fstat(fd, &st);
678
679     RtlEnterCriticalSection( &cache_section );
680     for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++)
681     {
682         if (empty == -1 &&
683             cdrom_cache[*dev].device == 0 &&
684             cdrom_cache[*dev].inode == 0)
685             empty = *dev;
686         else if (cdrom_cache[*dev].device == st.st_dev &&
687                  cdrom_cache[*dev].inode == st.st_ino)
688             break;
689     }
690     if (*dev == MAX_CACHE_ENTRIES)
691     {
692         if (empty == -1) ret = STATUS_NOT_IMPLEMENTED;
693         else
694         {
695             *dev = empty;
696             cdrom_cache[*dev].device  = st.st_dev;
697             cdrom_cache[*dev].inode   = st.st_ino;
698         }
699     }
700     RtlLeaveCriticalSection( &cache_section );
701
702     TRACE("%d, %d\n", *dev, fd);
703     return ret;
704 }
705
706 /******************************************************************
707  *              CDROM_GetStatusCode
708  *
709  *
710  */
711 static NTSTATUS CDROM_GetStatusCode(int io)
712 {
713     if (io == 0) return STATUS_SUCCESS;
714     return FILE_GetNtStatus();
715 }
716
717 /******************************************************************
718  *              CDROM_GetControl
719  *
720  */
721 static NTSTATUS CDROM_GetControl(int dev, int fd, CDROM_AUDIO_CONTROL* cac)
722 {
723 #ifdef __APPLE__
724     uint16_t speed;
725     int io = ioctl( fd, DKIOCCDGETSPEED, &speed );
726     if (io != 0) return CDROM_GetStatusCode( io );
727     /* DKIOCCDGETSPEED returns the speed in kilobytes per second,
728      * so convert to logical blocks (assumed to be ~2 KB).
729      */
730     cac->LogicalBlocksPerSecond = speed/2;
731 #else
732     cac->LogicalBlocksPerSecond = 1; /* FIXME */
733 #endif
734     cac->LbaFormat = 0; /* FIXME */
735     return  STATUS_NOT_SUPPORTED;
736 }
737
738 /******************************************************************
739  *              CDROM_GetDeviceNumber
740  *
741  */
742 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
743 {
744     FIXME( "stub\n" );
745     devnum->DeviceType = FILE_DEVICE_DISK;
746     devnum->DeviceNumber = 1;
747     devnum->PartitionNumber = 1;
748     return STATUS_SUCCESS;
749 }
750
751 /******************************************************************
752  *              CDROM_GetDriveGeometry
753  *
754  */
755 static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg)
756 {
757   CDROM_TOC     toc;
758   NTSTATUS      ret = 0;
759   int           fsize = 0;
760
761   if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
762
763   fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
764         - FRAME_OF_TOC(toc, 1); /* Total size in frames */
765   
766   dg->Cylinders.u.LowPart = fsize / (64 * 32); 
767   dg->Cylinders.u.HighPart = 0; 
768   dg->MediaType = RemovableMedia;  
769   dg->TracksPerCylinder = 64; 
770   dg->SectorsPerTrack = 32;  
771   dg->BytesPerSector= 2048; 
772   return ret;
773 }
774
775 /******************************************************************
776  *              CDROM_GetMediaType
777  *
778  */
779 static NTSTATUS CDROM_GetMediaType(int dev, GET_MEDIA_TYPES* medtype)
780 {
781     FIXME(": faking success\n");
782     medtype->DeviceType = FILE_DEVICE_CD_ROM;
783     medtype->MediaInfoCount = 0;
784     return STATUS_SUCCESS;
785 }
786
787 /**************************************************************************
788  *                              CDROM_Reset                     [internal]
789  */
790 static NTSTATUS CDROM_ResetAudio(int fd)
791 {
792 #if defined(linux)
793     return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
794 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
795     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
796 #else
797     FIXME("not supported on this O/S\n");
798     return STATUS_NOT_SUPPORTED;
799 #endif
800 }
801
802 /******************************************************************
803  *              CDROM_SetTray
804  *
805  *
806  */
807 static NTSTATUS CDROM_SetTray(int fd, BOOL doEject)
808 {
809 #if defined(linux)
810     return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
811 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
812     return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
813                                (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
814                                (ioctl(fd, CDIOCPREVENT, NULL)));
815 #elif defined(__APPLE__)
816     if (doEject) return CDROM_GetStatusCode( ioctl( fd, DKIOCEJECT, NULL ) );
817     else return STATUS_NOT_SUPPORTED;
818 #else
819     FIXME("not supported on this O/S\n");
820     return STATUS_NOT_SUPPORTED;
821 #endif
822 }
823
824 /******************************************************************
825  *              CDROM_ControlEjection
826  *
827  *
828  */
829 static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv)
830 {
831 #if defined(linux)
832     return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
833 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
834     return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
835 #else
836     FIXME("not supported on this O/S\n");
837     return STATUS_NOT_SUPPORTED;
838 #endif
839 }
840
841 /******************************************************************
842  *              CDROM_ReadTOC
843  *
844  *
845  */
846 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
847 {
848     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
849
850     if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
851         return STATUS_INVALID_PARAMETER;
852
853     RtlEnterCriticalSection( &cache_section );
854     if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd)))
855     {
856         *toc = cdrom_cache[dev].toc;
857         ret = STATUS_SUCCESS;
858     }
859     RtlLeaveCriticalSection( &cache_section );
860     return ret;
861 }
862
863 /******************************************************************
864  *              CDROM_GetDiskData
865  *
866  *
867  */
868 static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data)
869 {
870     CDROM_TOC   toc;
871     NTSTATUS    ret;
872     int         i;
873
874     if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
875     data->DiskData = 0;
876     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
877         if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
878             data->DiskData |= CDROM_DISK_DATA_TRACK;
879         else
880             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
881     }
882     return STATUS_SUCCESS;
883 }
884
885 /******************************************************************
886  *              CDROM_ReadQChannel
887  *
888  *
889  */
890 static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt,
891                                    SUB_Q_CHANNEL_DATA* data)
892 {
893     NTSTATUS            ret = STATUS_NOT_SUPPORTED;
894 #ifdef linux
895     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
896     int                 io;
897     struct cdrom_subchnl        sc;
898     sc.cdsc_format = CDROM_MSF;
899
900     io = ioctl(fd, CDROMSUBCHNL, &sc);
901     if (io == -1)
902     {
903         TRACE("opened or no_media (%s)!\n", strerror(errno));
904         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
905         CDROM_ClearCacheEntry(dev);
906         goto end;
907     }
908
909     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
910
911     switch (sc.cdsc_audiostatus) {
912     case CDROM_AUDIO_INVALID:
913         CDROM_ClearCacheEntry(dev);
914         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
915         break;
916     case CDROM_AUDIO_NO_STATUS:
917         CDROM_ClearCacheEntry(dev);
918         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
919         break;
920     case CDROM_AUDIO_PLAY:
921         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
922         break;
923     case CDROM_AUDIO_PAUSED:
924         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
925         break;
926     case CDROM_AUDIO_COMPLETED:
927         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
928         break;
929     case CDROM_AUDIO_ERROR:
930         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
931         break;
932     default:
933         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
934         break;
935     }
936     switch (fmt->Format)
937     {
938     case IOCTL_CDROM_CURRENT_POSITION:
939         RtlEnterCriticalSection( &cache_section );
940         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
941           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
942           data->CurrentPosition.Control = sc.cdsc_ctrl; 
943           data->CurrentPosition.ADR = sc.cdsc_adr; 
944           data->CurrentPosition.TrackNumber = sc.cdsc_trk; 
945           data->CurrentPosition.IndexNumber = sc.cdsc_ind; 
946
947           data->CurrentPosition.AbsoluteAddress[0] = 0; 
948           data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; 
949           data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
950           data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
951  
952           data->CurrentPosition.TrackRelativeAddress[0] = 0; 
953           data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; 
954           data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
955           data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
956
957           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
958         }
959         else /* not playing */
960         {
961           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
962           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
963         }
964         RtlLeaveCriticalSection( &cache_section );
965         break;
966     case IOCTL_CDROM_MEDIA_CATALOG:
967         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
968         {
969             struct cdrom_mcn mcn;
970             if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
971
972             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
973             data->MediaCatalog.Mcval = 0; /* FIXME */
974             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
975             data->MediaCatalog.MediaCatalog[14] = 0;
976         }
977         break;
978     case IOCTL_CDROM_TRACK_ISRC:
979         FIXME("TrackIsrc: NIY on linux\n");
980         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
981         data->TrackIsrc.Tcval = 0;
982         io = 0;
983         break;
984     }
985
986  end:
987     ret = CDROM_GetStatusCode(io);
988 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
989     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
990     int                 io;
991     struct ioc_read_subchannel  read_sc;
992     struct cd_sub_channel_info  sc;
993
994     read_sc.address_format = CD_MSF_FORMAT;
995     read_sc.track          = 0;
996     read_sc.data_len       = sizeof(sc);
997     read_sc.data           = &sc;
998     switch (fmt->Format)
999     {
1000     case IOCTL_CDROM_CURRENT_POSITION:
1001         read_sc.data_format    = CD_CURRENT_POSITION;
1002         break;
1003     case IOCTL_CDROM_MEDIA_CATALOG:
1004         read_sc.data_format    = CD_MEDIA_CATALOG;
1005         break;
1006     case IOCTL_CDROM_TRACK_ISRC:
1007         read_sc.data_format    = CD_TRACK_INFO;
1008         sc.what.track_info.track_number = data->TrackIsrc.Track;
1009         break;
1010     }
1011     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1012     if (io == -1)
1013     {
1014         TRACE("opened or no_media (%s)!\n", strerror(errno));
1015         CDROM_ClearCacheEntry(dev);
1016         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
1017         goto end;
1018     }
1019
1020     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
1021
1022     switch (sc.header.audio_status) {
1023     case CD_AS_AUDIO_INVALID:
1024         CDROM_ClearCacheEntry(dev);
1025         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
1026         break;
1027     case CD_AS_NO_STATUS:
1028         CDROM_ClearCacheEntry(dev);
1029         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
1030         break;
1031     case CD_AS_PLAY_IN_PROGRESS:
1032         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
1033         break;
1034     case CD_AS_PLAY_PAUSED:
1035         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
1036         break;
1037     case CD_AS_PLAY_COMPLETED:
1038         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
1039         break;
1040     case CD_AS_PLAY_ERROR:
1041         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
1042         break;
1043     default:
1044         TRACE("status=%02X !\n", sc.header.audio_status);
1045     }
1046     switch (fmt->Format)
1047     {
1048     case IOCTL_CDROM_CURRENT_POSITION:
1049         RtlEnterCriticalSection( &cache_section );
1050         if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
1051           data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
1052           data->CurrentPosition.Control = sc.what.position.control;
1053           data->CurrentPosition.ADR = sc.what.position.addr_type;
1054           data->CurrentPosition.TrackNumber = sc.what.position.track_number;
1055           data->CurrentPosition.IndexNumber = sc.what.position.index_number;
1056
1057           data->CurrentPosition.AbsoluteAddress[0] = 0;
1058           data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
1059           data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
1060           data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
1061           data->CurrentPosition.TrackRelativeAddress[0] = 0;
1062           data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
1063           data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
1064           data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
1065           cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
1066         }
1067         else { /* not playing */
1068           cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
1069           data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
1070         }
1071         RtlLeaveCriticalSection( &cache_section );
1072         break;
1073     case IOCTL_CDROM_MEDIA_CATALOG:
1074         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
1075         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
1076         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
1077         break;
1078     case IOCTL_CDROM_TRACK_ISRC:
1079         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
1080         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
1081         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
1082         break;
1083     }
1084
1085  end:
1086     ret = CDROM_GetStatusCode(io);
1087 #elif defined(__APPLE__)
1088     SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
1089     int io = 0;
1090     union
1091     {
1092         dk_cd_read_mcn_t mcn;
1093         dk_cd_read_isrc_t isrc;
1094     } ioc;
1095     /* We need IOCDAudioControl for IOCTL_CDROM_CURRENT_POSITION */
1096     if (fmt->Format == IOCTL_CDROM_CURRENT_POSITION)
1097     {
1098         FIXME("NIY\n");
1099         return STATUS_NOT_SUPPORTED;
1100     }
1101     /* No IOCDAudioControl support; just set the audio status to none */
1102     hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
1103     switch(fmt->Format)
1104     {
1105     case IOCTL_CDROM_MEDIA_CATALOG:
1106         if ((io = ioctl(fd, DKIOCCDREADMCN, &ioc.mcn)) == -1) break;
1107         memcpy(data->MediaCatalog.MediaCatalog, ioc.mcn.mcn, kCDMCNMaxLength);
1108         data->MediaCatalog.Mcval = 1;
1109         break;
1110     case IOCTL_CDROM_TRACK_ISRC:
1111         ioc.isrc.track = fmt->Track;
1112         if ((io = ioctl(fd, DKIOCCDREADISRC, &ioc.isrc)) == -1) break;
1113         memcpy(data->TrackIsrc.TrackIsrc, ioc.isrc.isrc, kCDISRCMaxLength);
1114         data->TrackIsrc.Tcval = 1;
1115         data->TrackIsrc.Track = ioc.isrc.track;
1116     }
1117     ret = CDROM_GetStatusCode(io);
1118 #endif
1119     return ret;
1120 }
1121
1122 /******************************************************************
1123  *              CDROM_Verify
1124  *  Implements: IOCTL_STORAGE_CHECK_VERIFY
1125  *              IOCTL_CDROM_CHECK_VERIFY
1126  *              IOCTL_DISK_CHECK_VERIFY
1127  *
1128  */
1129 static NTSTATUS CDROM_Verify(int dev, int fd)
1130 {
1131 #if defined(linux)
1132     int ret;
1133
1134     ret = ioctl(fd,  CDROM_DRIVE_STATUS, NULL);
1135     if(ret == -1) {
1136         TRACE("ioctl CDROM_DRIVE_STATUS failed(%s)!\n", strerror(errno));
1137         return CDROM_GetStatusCode(ret);
1138     }
1139
1140     if(ret == CDS_DISC_OK)
1141         return STATUS_SUCCESS;
1142     else
1143         return STATUS_NO_MEDIA_IN_DEVICE;
1144 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1145     int ret;
1146     ret = ioctl(fd, CDIOCSTART, NULL);
1147     if(ret == 0)
1148         return STATUS_SUCCESS;
1149     else
1150         return STATUS_NO_MEDIA_IN_DEVICE;
1151 #elif defined(__APPLE__)
1152         /* At this point, we know that we have media, because in Mac OS X, the
1153          * device file is only created when media is present. */
1154         return STATUS_SUCCESS;
1155 #else
1156     FIXME("not supported on this O/S\n");
1157     return STATUS_NOT_SUPPORTED;
1158 #endif
1159 }
1160
1161 /******************************************************************
1162  *              CDROM_PlayAudioMSF
1163  *
1164  *
1165  */
1166 static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf)
1167 {
1168     NTSTATUS       ret = STATUS_NOT_SUPPORTED;
1169 #ifdef linux
1170     struct      cdrom_msf       msf;
1171     int         io;
1172
1173     msf.cdmsf_min0   = audio_msf->StartingM;
1174     msf.cdmsf_sec0   = audio_msf->StartingS;
1175     msf.cdmsf_frame0 = audio_msf->StartingF;
1176     msf.cdmsf_min1   = audio_msf->EndingM;
1177     msf.cdmsf_sec1   = audio_msf->EndingS;
1178     msf.cdmsf_frame1 = audio_msf->EndingF;
1179
1180     io = ioctl(fd, CDROMSTART);
1181     if (io == -1)
1182     {
1183         WARN("motor doesn't start !\n");
1184         goto end;
1185     }
1186     io = ioctl(fd, CDROMPLAYMSF, &msf);
1187     if (io == -1)
1188     {
1189         WARN("device doesn't play !\n");
1190         goto end;
1191     }
1192     TRACE("msf = %d:%d:%d %d:%d:%d\n",
1193           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
1194           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
1195  end:
1196     ret = CDROM_GetStatusCode(io);
1197 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1198     struct      ioc_play_msf    msf;
1199     int         io;
1200
1201     msf.start_m      = audio_msf->StartingM;
1202     msf.start_s      = audio_msf->StartingS;
1203     msf.start_f      = audio_msf->StartingF;
1204     msf.end_m        = audio_msf->EndingM;
1205     msf.end_s        = audio_msf->EndingS;
1206     msf.end_f        = audio_msf->EndingF;
1207
1208     io = ioctl(fd, CDIOCSTART, NULL);
1209     if (io == -1)
1210     {
1211         WARN("motor doesn't start !\n");
1212         goto end;
1213     }
1214     io = ioctl(fd, CDIOCPLAYMSF, &msf);
1215     if (io == -1)
1216     {
1217         WARN("device doesn't play !\n");
1218         goto end;
1219     }
1220     TRACE("msf = %d:%d:%d %d:%d:%d\n",
1221           msf.start_m, msf.start_s, msf.start_f,
1222           msf.end_m,   msf.end_s,   msf.end_f);
1223 end:
1224     ret = CDROM_GetStatusCode(io);
1225 #endif
1226     return ret;
1227 }
1228
1229 /******************************************************************
1230  *              CDROM_SeekAudioMSF
1231  *
1232  *
1233  */
1234 static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1235 {
1236     CDROM_TOC toc;
1237     int i, io, frame;
1238     SUB_Q_CURRENT_POSITION *cp;
1239 #if defined(linux)
1240     struct cdrom_msf0   msf;
1241     struct cdrom_subchnl sc;
1242 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1243     struct ioc_play_msf msf;
1244     struct ioc_read_subchannel  read_sc;
1245     struct cd_sub_channel_info  sc;
1246     int final_frame;
1247 #endif
1248
1249     /* Use the information on the TOC to compute the new current
1250      * position, which is shadowed on the cache. [Portable]. */
1251     frame = FRAME_OF_MSF(*audio_msf);
1252
1253     if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io;
1254
1255     for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1256       if (FRAME_OF_TOC(toc,i)>frame) break;
1257     if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1258       return STATUS_INVALID_PARAMETER;
1259     i--;
1260     RtlEnterCriticalSection( &cache_section );
1261     cp = &cdrom_cache[dev].CurrentPosition;
1262     cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION; 
1263     cp->Control = toc.TrackData[i-toc.FirstTrack].Control; 
1264     cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; 
1265     cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1266     cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1267     cp->AbsoluteAddress[0] = 0; 
1268     cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1269     cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1270     cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1271     frame -= FRAME_OF_TOC(toc,i);
1272     cp->TrackRelativeAddress[0] = 0;
1273     MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
1274     RtlLeaveCriticalSection( &cache_section );
1275
1276     /* If playing, then issue a seek command, otherwise do nothing */
1277 #ifdef linux
1278     sc.cdsc_format = CDROM_MSF;
1279
1280     io = ioctl(fd, CDROMSUBCHNL, &sc);
1281     if (io == -1)
1282     {
1283         TRACE("opened or no_media (%s)!\n", strerror(errno));
1284         CDROM_ClearCacheEntry(dev);
1285         return CDROM_GetStatusCode(io);
1286     }
1287     if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1288     {
1289       msf.minute = audio_msf->M;
1290       msf.second = audio_msf->S;
1291       msf.frame  = audio_msf->F;
1292       return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf));
1293     }
1294     return STATUS_SUCCESS;
1295 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1296     read_sc.address_format = CD_MSF_FORMAT;
1297     read_sc.track          = 0;
1298     read_sc.data_len       = sizeof(sc);
1299     read_sc.data           = &sc;
1300     read_sc.data_format    = CD_CURRENT_POSITION;
1301
1302     io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1303     if (io == -1)
1304     {
1305         TRACE("opened or no_media (%s)!\n", strerror(errno));
1306         CDROM_ClearCacheEntry(dev);
1307         return CDROM_GetStatusCode(io);
1308     }
1309     if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) 
1310     {
1311
1312       msf.start_m      = audio_msf->M;
1313       msf.start_s      = audio_msf->S;
1314       msf.start_f      = audio_msf->F;
1315       final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1316       MSF_OF_FRAME(msf.end_m, final_frame);
1317
1318       return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
1319     }
1320     return STATUS_SUCCESS;
1321 #else
1322     FIXME("not supported on this O/S\n");
1323     return STATUS_NOT_SUPPORTED;
1324 #endif
1325 }
1326
1327 /******************************************************************
1328  *              CDROM_PauseAudio
1329  *
1330  *
1331  */
1332 static NTSTATUS CDROM_PauseAudio(int fd)
1333 {
1334 #if defined(linux)
1335     return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
1336 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1337     return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
1338 #else
1339     FIXME(": not supported on this O/S\n");
1340     return STATUS_NOT_SUPPORTED;
1341 #endif
1342 }
1343
1344 /******************************************************************
1345  *              CDROM_ResumeAudio
1346  *
1347  *
1348  */
1349 static NTSTATUS CDROM_ResumeAudio(int fd)
1350 {
1351 #if defined(linux)
1352     return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
1353 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1354     return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
1355 #else
1356     FIXME("not supported on this O/S\n");
1357     return STATUS_NOT_SUPPORTED;
1358 #endif
1359 }
1360
1361 /******************************************************************
1362  *              CDROM_StopAudio
1363  *
1364  *
1365  */
1366 static NTSTATUS CDROM_StopAudio(int fd)
1367 {
1368 #if defined(linux)
1369     return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
1370 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1371     return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
1372 #else
1373     FIXME("not supported on this O/S\n");
1374     return STATUS_NOT_SUPPORTED;
1375 #endif
1376 }
1377
1378 /******************************************************************
1379  *              CDROM_GetVolume
1380  *
1381  *
1382  */
1383 static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc)
1384 {
1385 #if defined(linux)
1386     struct cdrom_volctrl volc;
1387     int io;
1388
1389     io = ioctl(fd, CDROMVOLREAD, &volc);
1390     if (io != -1)
1391     {
1392         vc->PortVolume[0] = volc.channel0;
1393         vc->PortVolume[1] = volc.channel1;
1394         vc->PortVolume[2] = volc.channel2;
1395         vc->PortVolume[3] = volc.channel3;
1396     }
1397     return CDROM_GetStatusCode(io);
1398 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1399     struct  ioc_vol     volc;
1400     int io;
1401
1402     io = ioctl(fd, CDIOCGETVOL, &volc);
1403     if (io != -1)
1404     {
1405         vc->PortVolume[0] = volc.vol[0];
1406         vc->PortVolume[1] = volc.vol[1];
1407         vc->PortVolume[2] = volc.vol[2];
1408         vc->PortVolume[3] = volc.vol[3];
1409     }
1410     return CDROM_GetStatusCode(io);
1411 #else
1412     FIXME("not supported on this O/S\n");
1413     return STATUS_NOT_SUPPORTED;
1414 #endif
1415 }
1416
1417 /******************************************************************
1418  *              CDROM_SetVolume
1419  *
1420  *
1421  */
1422 static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
1423 {
1424 #if defined(linux)
1425     struct cdrom_volctrl volc;
1426
1427     volc.channel0 = vc->PortVolume[0];
1428     volc.channel1 = vc->PortVolume[1];
1429     volc.channel2 = vc->PortVolume[2];
1430     volc.channel3 = vc->PortVolume[3];
1431
1432     return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc));
1433 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1434     struct  ioc_vol     volc;
1435
1436     volc.vol[0] = vc->PortVolume[0];
1437     volc.vol[1] = vc->PortVolume[1];
1438     volc.vol[2] = vc->PortVolume[2];
1439     volc.vol[3] = vc->PortVolume[3];
1440
1441     return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
1442 #else
1443     FIXME(": not supported on this O/S\n");
1444     return STATUS_NOT_SUPPORTED;
1445 #endif
1446 }
1447
1448 /******************************************************************
1449  *              CDROM_RawRead
1450  *
1451  * Some features of this IOCTL are rather poorly documented and
1452  * not really intuitive either:
1453  *
1454  *   1. Although the DiskOffset parameter is meant to be a
1455  *      byte offset into the disk, it is in fact the sector
1456  *      number multiplied by 2048 regardless of the actual
1457  *      sector size.
1458  *
1459  *   2. The least significant 11 bits of DiskOffset are ignored.
1460  *
1461  *   3. The TrackMode parameter has no effect on the sector
1462  *      size. The entire raw sector (i.e. 2352 bytes of data)
1463  *      is always returned. IMO the TrackMode is only used
1464  *      to check the correct sector type.
1465  *
1466  */
1467 static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1468 {
1469     int         ret = STATUS_NOT_SUPPORTED;
1470     int         io = -1;
1471 #ifdef __APPLE__
1472     dk_cd_read_t cdrd;
1473 #endif
1474
1475     TRACE("RAW_READ_INFO: DiskOffset=%i,%i SectorCount=%i TrackMode=%i\n buffer=%p len=%i sz=%p\n",
1476           raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz);
1477
1478     if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL;
1479
1480 #if defined(linux)
1481     if (raw->DiskOffset.u.HighPart & ~2047) {
1482         WARN("DiskOffset points to a sector >= 2**32\n");
1483         return ret;
1484     }
1485
1486     switch (raw->TrackMode)
1487     {
1488     case YellowMode2:
1489     case XAForm2:
1490     {
1491         DWORD lba = raw->DiskOffset.QuadPart >> 11;
1492         struct cdrom_msf*       msf;
1493         PBYTE                   *bp; /* current buffer pointer */
1494         DWORD i;
1495
1496         if ((lba + raw->SectorCount) >
1497             ((1 << 8*sizeof(msf->cdmsf_min0)) * CD_SECS * CD_FRAMES
1498              - CD_MSF_OFFSET)) {
1499             WARN("DiskOffset not accessible with MSF\n");
1500             return ret;
1501         }
1502
1503         /* Linux reads only one sector at a time.
1504          * ioctl CDROMREADRAW takes struct cdrom_msf as an argument
1505          * on the contrary to what header comments state.
1506          */
1507         lba += CD_MSF_OFFSET;
1508         for (i = 0, bp = buffer; i < raw->SectorCount;
1509              i++, lba++, bp += 2352)
1510         {
1511             msf = (struct cdrom_msf*)bp;
1512             msf->cdmsf_min0   = lba / CD_FRAMES / CD_SECS;
1513             msf->cdmsf_sec0   = lba / CD_FRAMES % CD_SECS;
1514             msf->cdmsf_frame0 = lba % CD_FRAMES;
1515             io = ioctl(fd, CDROMREADRAW, msf);
1516             if (io != 0)
1517             {
1518                 *sz = 2352 * i;
1519                 return CDROM_GetStatusCode(io);
1520             }
1521         }
1522         break;
1523     }
1524
1525     case CDDA:
1526     {
1527         struct cdrom_read_audio cdra;
1528
1529         cdra.addr.lba = raw->DiskOffset.QuadPart >> 11;
1530         TRACE("reading at %u\n", cdra.addr.lba);
1531         cdra.addr_format = CDROM_LBA;
1532         cdra.nframes = raw->SectorCount;
1533         cdra.buf = buffer;
1534         io = ioctl(fd, CDROMREADAUDIO, &cdra);
1535         break;
1536     }
1537
1538     default:
1539         FIXME("NIY: %d\n", raw->TrackMode);
1540         return STATUS_INVALID_PARAMETER;
1541     }
1542 #elif defined(__APPLE__)
1543     /* Mac OS lets us read multiple parts of the sector at a time.
1544      * We can read all the sectors in at once, unlike Linux.
1545      */
1546     memset(&cdrd, 0, sizeof(cdrd));
1547     cdrd.offset = (raw->DiskOffset.QuadPart >> 11) * kCDSectorSizeWhole;
1548     cdrd.buffer = buffer;
1549     cdrd.bufferLength = raw->SectorCount * kCDSectorSizeWhole;
1550     switch (raw->TrackMode)
1551     {
1552     case YellowMode2:
1553         cdrd.sectorType = kCDSectorTypeMode2;
1554         cdrd.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader | kCDSectorAreaUser;
1555         break;
1556
1557     case XAForm2:
1558         cdrd.sectorType = kCDSectorTypeMode2Form2;
1559         cdrd.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader | kCDSectorAreaSubHeader | kCDSectorAreaUser;
1560         break;
1561
1562     case CDDA:
1563         cdrd.sectorType = kCDSectorTypeCDDA;
1564         cdrd.sectorArea = kCDSectorAreaUser;
1565         break;
1566
1567     default:
1568         FIXME("NIY: %d\n", raw->TrackMode);
1569         return STATUS_INVALID_PARAMETER;
1570     }
1571     io = ioctl(fd, DKIOCCDREAD, &cdrd);
1572     if (io != 0)
1573     {
1574         *sz = cdrd.bufferLength;
1575         return CDROM_GetStatusCode(io);
1576     }
1577 #else
1578     switch (raw->TrackMode)
1579     {
1580     case YellowMode2:
1581         FIXME("YellowMode2: NIY\n");
1582         return ret;
1583     case XAForm2:
1584         FIXME("XAForm2: NIY\n");
1585         return ret;
1586     case CDDA:
1587         FIXME("CDDA: NIY\n");
1588         return ret;
1589     default:
1590         FIXME("NIY: %d\n", raw->TrackMode);
1591         return STATUS_INVALID_PARAMETER;
1592     }
1593 #endif
1594
1595     *sz = 2352 * raw->SectorCount;
1596     ret = CDROM_GetStatusCode(io);
1597     return ret;
1598 }
1599
1600 /******************************************************************
1601  *        CDROM_ScsiPassThroughDirect
1602  *        Implements IOCTL_SCSI_PASS_THROUGH_DIRECT
1603  *
1604  */
1605 static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket)
1606 {
1607     int ret = STATUS_NOT_SUPPORTED;
1608 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1609     sg_io_hdr_t cmd;
1610     int io;
1611 #elif defined HAVE_SCSIREQ_T_CMD
1612     scsireq_t cmd;
1613     int io;
1614 #elif defined __APPLE__
1615     dk_scsi_command_t cmd;
1616     int io;
1617 #endif
1618
1619     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1620         return STATUS_BUFFER_TOO_SMALL;
1621
1622     if (pPacket->CdbLength > 16)
1623         return STATUS_INVALID_PARAMETER;
1624
1625 #ifdef SENSEBUFLEN
1626     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1627         return STATUS_INVALID_PARAMETER;
1628 #elif defined HAVE_REQUEST_SENSE
1629     if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1630         return STATUS_INVALID_PARAMETER;
1631 #endif
1632
1633     if (pPacket->DataTransferLength > 0 && !pPacket->DataBuffer)
1634         return STATUS_INVALID_PARAMETER;
1635
1636 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1637     RtlZeroMemory(&cmd, sizeof(cmd));
1638
1639     cmd.interface_id   = 'S';
1640     cmd.cmd_len        = pPacket->CdbLength;
1641     cmd.mx_sb_len      = pPacket->SenseInfoLength;
1642     cmd.dxfer_len      = pPacket->DataTransferLength;
1643     cmd.dxferp         = pPacket->DataBuffer;
1644     cmd.cmdp           = pPacket->Cdb;
1645     cmd.sbp            = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1646     cmd.timeout        = pPacket->TimeOutValue*1000;
1647
1648     switch (pPacket->DataIn)
1649     {
1650     case SCSI_IOCTL_DATA_IN:
1651         cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1652         break;
1653     case SCSI_IOCTL_DATA_OUT:
1654         cmd.dxfer_direction = SG_DXFER_TO_DEV;
1655         break;
1656     case SCSI_IOCTL_DATA_UNSPECIFIED:
1657         cmd.dxfer_direction = SG_DXFER_NONE;
1658         break;
1659     default:
1660        return STATUS_INVALID_PARAMETER;
1661     }
1662
1663     io = ioctl(fd, SG_IO, &cmd);
1664
1665     pPacket->ScsiStatus         = cmd.status;
1666     pPacket->DataTransferLength = cmd.resid;
1667     pPacket->SenseInfoLength    = cmd.sb_len_wr;
1668
1669     ret = CDROM_GetStatusCode(io);
1670
1671 #elif defined HAVE_SCSIREQ_T_CMD
1672
1673     memset(&cmd, 0, sizeof(cmd));
1674     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1675
1676     cmd.cmdlen         = pPacket->CdbLength;
1677     cmd.databuf        = pPacket->DataBuffer;
1678     cmd.datalen        = pPacket->DataTransferLength;
1679     cmd.senselen       = pPacket->SenseInfoLength;
1680     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1681
1682     switch (pPacket->DataIn)
1683     {
1684     case SCSI_IOCTL_DATA_OUT:
1685         cmd.flags |= SCCMD_WRITE;
1686         break;
1687     case SCSI_IOCTL_DATA_IN:
1688         cmd.flags |= SCCMD_READ;
1689         break;
1690     case SCSI_IOCTL_DATA_UNSPECIFIED:
1691         cmd.flags = 0;
1692         break;
1693     default:
1694        return STATUS_INVALID_PARAMETER;
1695     }
1696
1697     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1698
1699     switch (cmd.retsts)
1700     {
1701     case SCCMD_OK:         break;
1702     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1703                            break;
1704     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1705                            break;
1706     case SCCMD_SENSE:      break;
1707     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1708                            break;
1709     }
1710
1711     if (pPacket->SenseInfoLength != 0)
1712     {
1713         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1714                cmd.sense, pPacket->SenseInfoLength);
1715     }
1716
1717     pPacket->ScsiStatus = cmd.status;
1718
1719     ret = CDROM_GetStatusCode(io);
1720
1721 #elif defined(__APPLE__)
1722
1723     memset(&cmd, 0, sizeof(cmd));
1724     memcpy(cmd.cdb, pPacket->Cdb, pPacket->CdbLength);
1725
1726     cmd.cdbSize        = pPacket->CdbLength;
1727     cmd.buffer         = pPacket->DataBuffer;
1728     cmd.bufferSize     = pPacket->DataTransferLength;
1729     cmd.sense          = (char*)pPacket + pPacket->SenseInfoOffset;
1730     cmd.senseLen       = pPacket->SenseInfoLength;
1731     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1732
1733     switch (pPacket->DataIn)
1734     {
1735     case SCSI_IOCTL_DATA_OUT:
1736         cmd.direction = kSCSIDataTransfer_FromInitiatorToTarget;
1737         break;
1738     case SCSI_IOCTL_DATA_IN:
1739         cmd.direction = kSCSIDataTransfer_FromTargetToInitiator;
1740         break;
1741     case SCSI_IOCTL_DATA_UNSPECIFIED:
1742         cmd.direction = kSCSIDataTransfer_NoDataTransfer;
1743         break;
1744     default:
1745        return STATUS_INVALID_PARAMETER;
1746     }
1747
1748     io = ioctl(fd, DKIOCSCSICOMMAND, &cmd);
1749
1750     if (cmd.response == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE)
1751     {
1752         /* Command failed */
1753         switch (cmd.status)
1754         {
1755         case kSCSITaskStatus_TaskTimeoutOccurred:     return STATUS_TIMEOUT;
1756                                                       break;
1757         case kSCSITaskStatus_ProtocolTimeoutOccurred: return STATUS_IO_TIMEOUT;
1758                                                       break;
1759         case kSCSITaskStatus_DeviceNotResponding:     return STATUS_DEVICE_BUSY;
1760                                                       break;
1761         case kSCSITaskStatus_DeviceNotPresent:
1762             return STATUS_NO_SUCH_DEVICE;
1763             break;
1764         case kSCSITaskStatus_DeliveryFailure:
1765             return STATUS_DEVICE_PROTOCOL_ERROR;
1766             break;
1767         case kSCSITaskStatus_No_Status:
1768         default:
1769             return STATUS_UNSUCCESSFUL;
1770             break;
1771         }
1772     }
1773
1774     if (cmd.status != kSCSITaskStatus_No_Status)
1775         pPacket->ScsiStatus = cmd.status;
1776
1777     ret = CDROM_GetStatusCode(io);
1778 #endif
1779     return ret;
1780 }
1781
1782 /******************************************************************
1783  *              CDROM_ScsiPassThrough
1784  *              Implements IOCTL_SCSI_PASS_THROUGH
1785  *
1786  */
1787 static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket)
1788 {
1789     int ret = STATUS_NOT_SUPPORTED;
1790 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1791     sg_io_hdr_t cmd;
1792     int io;
1793 #elif defined HAVE_SCSIREQ_T_CMD
1794     scsireq_t cmd;
1795     int io;
1796 #elif defined __APPLE__
1797     dk_scsi_command_t cmd;
1798     int io;
1799 #endif
1800
1801     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1802         return STATUS_BUFFER_TOO_SMALL;
1803
1804     if (pPacket->CdbLength > 16)
1805         return STATUS_INVALID_PARAMETER;
1806
1807 #ifdef SENSEBUFLEN
1808     if (pPacket->SenseInfoLength > SENSEBUFLEN)
1809         return STATUS_INVALID_PARAMETER;
1810 #elif defined HAVE_REQUEST_SENSE
1811     if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1812         return STATUS_INVALID_PARAMETER;
1813 #endif
1814
1815     if (pPacket->DataTransferLength > 0 && pPacket->DataBufferOffset < sizeof(SCSI_PASS_THROUGH))
1816         return STATUS_INVALID_PARAMETER;
1817
1818 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1819     RtlZeroMemory(&cmd, sizeof(cmd));
1820
1821     cmd.interface_id   = 'S';
1822     cmd.dxfer_len      = pPacket->DataTransferLength;
1823     cmd.dxferp         = (char*)pPacket + pPacket->DataBufferOffset;
1824     cmd.cmd_len        = pPacket->CdbLength;
1825     cmd.cmdp           = pPacket->Cdb;
1826     cmd.mx_sb_len      = pPacket->SenseInfoLength;
1827     cmd.timeout        = pPacket->TimeOutValue*1000;
1828
1829     if(cmd.mx_sb_len > 0)
1830         cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1831
1832     switch (pPacket->DataIn)
1833     {
1834     case SCSI_IOCTL_DATA_IN:
1835         cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1836                              break;
1837     case SCSI_IOCTL_DATA_OUT:
1838         cmd.dxfer_direction = SG_DXFER_TO_DEV;
1839                              break;
1840     case SCSI_IOCTL_DATA_UNSPECIFIED:
1841         cmd.dxfer_direction = SG_DXFER_NONE;
1842                              break;
1843     default:
1844        return STATUS_INVALID_PARAMETER;
1845     }
1846
1847     io = ioctl(fd, SG_IO, &cmd);
1848
1849     pPacket->ScsiStatus         = cmd.status;
1850     pPacket->DataTransferLength = cmd.resid;
1851     pPacket->SenseInfoLength    = cmd.sb_len_wr;
1852
1853     ret = CDROM_GetStatusCode(io);
1854
1855 #elif defined HAVE_SCSIREQ_T_CMD
1856
1857     memset(&cmd, 0, sizeof(cmd));
1858     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1859
1860     if ( pPacket->DataBufferOffset > 0x1000 )
1861     {
1862         cmd.databuf     = (void*)pPacket->DataBufferOffset;
1863     }
1864     else
1865     {
1866         cmd.databuf     = (char*)pPacket + pPacket->DataBufferOffset;
1867     }
1868
1869     cmd.cmdlen         = pPacket->CdbLength;
1870     cmd.datalen        = pPacket->DataTransferLength;
1871     cmd.senselen       = pPacket->SenseInfoLength;
1872     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1873
1874     switch (pPacket->DataIn)
1875     {
1876     case SCSI_IOCTL_DATA_OUT:
1877         cmd.flags |= SCCMD_WRITE;
1878         break;
1879     case SCSI_IOCTL_DATA_IN:
1880         cmd.flags |= SCCMD_READ;
1881         break;
1882     case SCSI_IOCTL_DATA_UNSPECIFIED:
1883         cmd.flags = 0;
1884         break;
1885     default:
1886        return STATUS_INVALID_PARAMETER;
1887     }
1888
1889     io = ioctl(fd, SCIOCCOMMAND, &cmd);
1890
1891     switch (cmd.retsts)
1892     {
1893     case SCCMD_OK:         break;
1894     case SCCMD_TIMEOUT:    return STATUS_TIMEOUT;
1895                            break;
1896     case SCCMD_BUSY:       return STATUS_DEVICE_BUSY;
1897                            break;
1898     case SCCMD_SENSE:      break;
1899     case SCCMD_UNKNOWN:    return STATUS_UNSUCCESSFUL;
1900                            break;
1901     }
1902
1903     if (pPacket->SenseInfoLength != 0)
1904     {
1905         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1906                cmd.sense, pPacket->SenseInfoLength);
1907     }
1908
1909     pPacket->ScsiStatus = cmd.status;
1910
1911     ret = CDROM_GetStatusCode(io);
1912
1913 #elif defined(__APPLE__)
1914
1915     memset(&cmd, 0, sizeof(cmd));
1916     memcpy(cmd.cdb, pPacket->Cdb, pPacket->CdbLength);
1917
1918     cmd.cdbSize        = pPacket->CdbLength;
1919     cmd.buffer         = (char*)pPacket + pPacket->DataBufferOffset;
1920     cmd.bufferSize     = pPacket->DataTransferLength;
1921     cmd.sense          = (char*)pPacket + pPacket->SenseInfoOffset;
1922     cmd.senseLen       = pPacket->SenseInfoLength;
1923     cmd.timeout        = pPacket->TimeOutValue*1000; /* in milliseconds */
1924
1925     switch (pPacket->DataIn)
1926     {
1927     case SCSI_IOCTL_DATA_OUT:
1928         cmd.direction = kSCSIDataTransfer_FromInitiatorToTarget;
1929         break;
1930     case SCSI_IOCTL_DATA_IN:
1931         cmd.direction = kSCSIDataTransfer_FromTargetToInitiator;
1932         break;
1933     case SCSI_IOCTL_DATA_UNSPECIFIED:
1934         cmd.direction = kSCSIDataTransfer_NoDataTransfer;
1935         break;
1936     default:
1937        return STATUS_INVALID_PARAMETER;
1938     }
1939
1940     io = ioctl(fd, DKIOCSCSICOMMAND, &cmd);
1941
1942     if (cmd.response == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE)
1943     {
1944         /* Command failed */
1945         switch (cmd.status)
1946         {
1947         case kSCSITaskStatus_TaskTimeoutOccurred:
1948             return STATUS_TIMEOUT;
1949             break;
1950         case kSCSITaskStatus_ProtocolTimeoutOccurred:
1951             return STATUS_IO_TIMEOUT;
1952             break;
1953         case kSCSITaskStatus_DeviceNotResponding:
1954             return STATUS_DEVICE_BUSY;
1955             break;
1956         case kSCSITaskStatus_DeviceNotPresent:
1957             return STATUS_NO_SUCH_DEVICE;
1958             break;
1959         case kSCSITaskStatus_DeliveryFailure:
1960             return STATUS_DEVICE_PROTOCOL_ERROR;
1961             break;
1962         case kSCSITaskStatus_No_Status:
1963         default:
1964             return STATUS_UNSUCCESSFUL;
1965             break;
1966         }
1967     }
1968
1969     if (cmd.status != kSCSITaskStatus_No_Status)
1970         pPacket->ScsiStatus = cmd.status;
1971
1972     ret = CDROM_GetStatusCode(io);
1973 #endif
1974     return ret;
1975 }
1976
1977 /******************************************************************
1978  *              CDROM_ScsiGetCaps
1979  *
1980  *
1981  */
1982 static NTSTATUS CDROM_ScsiGetCaps(int fd, PIO_SCSI_CAPABILITIES caps)
1983 {
1984     NTSTATUS    ret = STATUS_NOT_IMPLEMENTED;
1985
1986 #ifdef SG_SCATTER_SZ
1987     caps->Length = sizeof(*caps);
1988     caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1989     caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1990     caps->SupportedAsynchronousEvents = TRUE;
1991     caps->AlignmentMask = getpagesize();
1992     caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1993     caps->AdapterScansDown = FALSE; /* FIXME ? */
1994     caps->AdapterUsesPio = FALSE; /* FIXME ? */
1995     ret = STATUS_SUCCESS;
1996 #elif defined __APPLE__
1997     uint64_t bytesr, bytesw, align;
1998     int io = ioctl(fd, DKIOCGETMAXBYTECOUNTREAD, &bytesr);
1999     if (io != 0) return CDROM_GetStatusCode(io);
2000     io = ioctl(fd, DKIOCGETMAXBYTECOUNTWRITE, &bytesw);
2001     if (io != 0) return CDROM_GetStatusCode(io);
2002     io = ioctl(fd, DKIOCGETMINSEGMENTALIGNMENTBYTECOUNT, &align);
2003     if (io != 0) return CDROM_GetStatusCode(io);
2004     caps->Length = sizeof(*caps);
2005     caps->MaximumTransferLength = bytesr < bytesw ? bytesr : bytesw;
2006     caps->MaximumPhysicalPages = caps->MaximumTransferLength / getpagesize();
2007     caps->SupportedAsynchronousEvents = TRUE;
2008     caps->AlignmentMask = align-1;
2009     caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
2010     caps->AdapterScansDown = FALSE; /* FIXME ? */
2011     caps->AdapterUsesPio = FALSE; /* FIXME ? */
2012     ret = STATUS_SUCCESS;
2013 #else
2014     FIXME("Unimplemented\n");
2015 #endif
2016     return ret;
2017 }
2018
2019 /******************************************************************
2020  *              CDROM_GetAddress
2021  *
2022  * implements IOCTL_SCSI_GET_ADDRESS
2023  */
2024 static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address)
2025 {
2026     UCHAR portnum, busid, targetid, lun;
2027
2028     address->Length = sizeof(SCSI_ADDRESS);
2029     if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
2030         return STATUS_NOT_SUPPORTED;
2031
2032     address->PortNumber = portnum; /* primary=0 secondary=1 for ide */
2033     address->PathId = busid;       /* always 0 for ide */
2034     address->TargetId = targetid;  /* master=0 slave=1 for ide */
2035     address->Lun = lun;
2036     return STATUS_SUCCESS;
2037 }
2038
2039 /******************************************************************
2040  *              DVD_StartSession
2041  *
2042  *
2043  */
2044 static NTSTATUS DVD_StartSession(int fd, const DVD_SESSION_ID *sid_in, PDVD_SESSION_ID sid_out)
2045 {
2046 #if defined(linux)
2047     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2048     dvd_authinfo auth_info;
2049
2050     memset( &auth_info, 0, sizeof( auth_info ) );
2051     auth_info.type = DVD_LU_SEND_AGID;
2052     if (sid_in) auth_info.lsa.agid = *(const int*)sid_in; /* ?*/
2053
2054     TRACE("fd 0x%08x\n",fd);
2055     ret =CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
2056     *sid_out = auth_info.lsa.agid;
2057     return ret;
2058 #elif defined(__APPLE__)
2059     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2060     dk_dvd_report_key_t dvdrk;
2061     DVDAuthenticationGrantIDInfo agid_info;
2062
2063     dvdrk.format = kDVDKeyFormatAGID_CSS;
2064     dvdrk.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2065     if(sid_in) dvdrk.grantID = *(uint8_t*)sid_in; /* ? */
2066     dvdrk.bufferLength = sizeof(DVDAuthenticationGrantIDInfo);
2067     dvdrk.buffer = &agid_info;
2068
2069     ret = CDROM_GetStatusCode(ioctl(fd, DKIOCDVDREPORTKEY, &dvdrk));
2070     *sid_out = agid_info.grantID;
2071     return ret;
2072 #else
2073     FIXME("not supported on this O/S\n");
2074     return STATUS_NOT_SUPPORTED;
2075 #endif
2076 }
2077
2078 /******************************************************************
2079  *              DVD_EndSession
2080  *
2081  *
2082  */
2083 static NTSTATUS DVD_EndSession(int fd, const DVD_SESSION_ID *sid)
2084 {
2085 #if defined(linux)
2086     dvd_authinfo auth_info;
2087
2088     memset( &auth_info, 0, sizeof( auth_info ) );
2089     auth_info.type = DVD_INVALIDATE_AGID;
2090     auth_info.lsa.agid = *(const int*)sid;
2091
2092     TRACE("\n");
2093     return CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
2094 #elif defined(__APPLE__)
2095     dk_dvd_send_key_t dvdsk;
2096
2097     dvdsk.format = kDVDKeyFormatAGID_Invalidate;
2098     dvdsk.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2099     dvdsk.grantID = (uint8_t)*sid;
2100
2101     return CDROM_GetStatusCode(ioctl(fd, DKIOCDVDSENDKEY, &dvdsk));
2102 #else
2103     FIXME("not supported on this O/S\n");
2104     return STATUS_NOT_SUPPORTED;
2105 #endif
2106 }
2107
2108 /******************************************************************
2109  *              DVD_SendKey
2110  *
2111  *
2112  */
2113 static NTSTATUS DVD_SendKey(int fd, const DVD_COPY_PROTECT_KEY *key)
2114 {
2115 #if defined(linux)
2116     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2117     dvd_authinfo auth_info;
2118
2119     memset( &auth_info, 0, sizeof( auth_info ) );
2120     switch (key->KeyType)
2121     {
2122     case DvdChallengeKey:
2123         auth_info.type = DVD_HOST_SEND_CHALLENGE;
2124         auth_info.hsc.agid = (int)key->SessionId;
2125         TRACE("DvdChallengeKey ioc 0x%x\n", DVD_AUTH );
2126         memcpy( auth_info.hsc.chal, key->KeyData, DVD_CHALLENGE_SIZE );
2127         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2128         break;
2129     case DvdBusKey2:
2130         auth_info.type = DVD_HOST_SEND_KEY2;
2131         auth_info.hsk.agid = (int)key->SessionId;
2132
2133         memcpy( auth_info.hsk.key, key->KeyData, DVD_KEY_SIZE );
2134         
2135         TRACE("DvdBusKey2\n");
2136         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2137         break;
2138
2139     default:
2140         FIXME("Unknown Keytype 0x%x\n",key->KeyType);
2141     }
2142     return ret;
2143 #elif defined(__APPLE__)
2144     dk_dvd_send_key_t dvdsk;
2145     union
2146     {
2147         DVDChallengeKeyInfo chal;
2148         DVDKey2Info key2;
2149     } key_desc;
2150
2151     switch(key->KeyType)
2152     {
2153     case DvdChallengeKey:
2154         dvdsk.format = kDVDKeyFormatChallengeKey;
2155         dvdsk.bufferLength = sizeof(key_desc.chal);
2156         dvdsk.buffer = &key_desc.chal;
2157         OSWriteBigInt16(key_desc.chal.dataLength, 0, key->KeyLength);
2158         memcpy(key_desc.chal.challengeKeyValue, key->KeyData, key->KeyLength);
2159         break;
2160     case DvdBusKey2:
2161         dvdsk.format = kDVDKeyFormatKey2;
2162         dvdsk.bufferLength = sizeof(key_desc.key2);
2163         dvdsk.buffer = &key_desc.key2;
2164         OSWriteBigInt16(key_desc.key2.dataLength, 0, key->KeyLength);
2165         memcpy(key_desc.key2.key2Value, key->KeyData, key->KeyLength);
2166         break;
2167     case DvdInvalidateAGID:
2168         dvdsk.format = kDVDKeyFormatAGID_Invalidate;
2169         break;
2170     case DvdBusKey1:
2171     case DvdTitleKey:
2172     case DvdAsf:
2173     case DvdGetRpcKey:
2174     case DvdDiskKey:
2175         ERR("attempted to write read-only key type 0x%x\n", key->KeyType);
2176         return STATUS_NOT_SUPPORTED;
2177     case DvdSetRpcKey:
2178         FIXME("DvdSetRpcKey NIY\n");
2179         return STATUS_NOT_SUPPORTED;
2180     default:
2181         FIXME("got unknown key type 0x%x\n", key->KeyType);
2182         return STATUS_NOT_SUPPORTED;
2183     }
2184     dvdsk.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2185     dvdsk.grantID = (uint8_t)key->SessionId;
2186
2187     return CDROM_GetStatusCode(ioctl(fd, DKIOCDVDSENDKEY, &dvdsk));
2188 #else
2189     FIXME("not supported on this O/S\n");
2190     return STATUS_NOT_SUPPORTED;
2191 #endif    
2192 }
2193
2194 /******************************************************************
2195  *              DVD_ReadKey
2196  *
2197  *
2198  */
2199 static NTSTATUS DVD_ReadKey(int fd, PDVD_COPY_PROTECT_KEY key)
2200 {
2201 #if defined(linux)
2202     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2203     dvd_struct dvd;
2204     dvd_authinfo auth_info;
2205
2206     memset( &dvd, 0, sizeof( dvd_struct ) );
2207     memset( &auth_info, 0, sizeof( auth_info ) );
2208     switch (key->KeyType)
2209     {
2210     case DvdDiskKey:
2211         
2212         dvd.type = DVD_STRUCT_DISCKEY;
2213         dvd.disckey.agid = (int)key->SessionId;
2214         memset( dvd.disckey.value, 0, DVD_DISCKEY_SIZE );
2215         
2216         TRACE("DvdDiskKey\n");
2217         ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd ));
2218         if (ret == STATUS_SUCCESS)
2219             memcpy(key->KeyData,dvd.disckey.value,DVD_DISCKEY_SIZE);
2220         break;
2221     case DvdTitleKey:
2222         auth_info.type = DVD_LU_SEND_TITLE_KEY;
2223         auth_info.lstk.agid = (int)key->SessionId;
2224         auth_info.lstk.lba = (int)(key->Parameters.TitleOffset.QuadPart>>11);
2225         TRACE("DvdTitleKey session %d Quadpart 0x%08lx offset 0x%08x\n",
2226               (int)key->SessionId, (long)key->Parameters.TitleOffset.QuadPart, 
2227               auth_info.lstk.lba);
2228         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2229         if (ret == STATUS_SUCCESS)
2230             memcpy(key->KeyData, auth_info.lstk.title_key, DVD_KEY_SIZE );
2231         break;
2232     case DvdChallengeKey:
2233
2234         auth_info.type = DVD_LU_SEND_CHALLENGE;
2235         auth_info.lsc.agid = (int)key->SessionId;
2236
2237         TRACE("DvdChallengeKey\n");
2238         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2239         if (ret == STATUS_SUCCESS)
2240             memcpy( key->KeyData, auth_info.lsc.chal, DVD_CHALLENGE_SIZE );
2241         break;
2242     case DvdAsf:
2243         auth_info.type = DVD_LU_SEND_ASF;
2244         TRACE("DvdAsf\n");
2245         auth_info.lsasf.asf=((PDVD_ASF)key->KeyData)->SuccessFlag;
2246         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2247         ((PDVD_ASF)key->KeyData)->SuccessFlag = auth_info.lsasf.asf;
2248         break;
2249     case DvdBusKey1:
2250         auth_info.type = DVD_LU_SEND_KEY1;
2251         auth_info.lsk.agid = (int)key->SessionId;
2252
2253         TRACE("DvdBusKey1\n");
2254         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2255
2256         if (ret == STATUS_SUCCESS)
2257             memcpy( key->KeyData, auth_info.lsk.key, DVD_KEY_SIZE );
2258         break;
2259     case DvdGetRpcKey:
2260         auth_info.type = DVD_LU_SEND_RPC_STATE;
2261
2262         TRACE("DvdGetRpcKey\n");
2263         ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2264
2265         if (ret == STATUS_SUCCESS)
2266         {
2267             ((PDVD_RPC_KEY)key->KeyData)->TypeCode = auth_info.lrpcs.type;
2268             ((PDVD_RPC_KEY)key->KeyData)->RegionMask = auth_info.lrpcs.region_mask;
2269             ((PDVD_RPC_KEY)key->KeyData)->RpcScheme = auth_info.lrpcs.rpc_scheme;
2270             ((PDVD_RPC_KEY)key->KeyData)->UserResetsAvailable = auth_info.lrpcs.ucca;
2271             ((PDVD_RPC_KEY)key->KeyData)->ManufacturerResetsAvailable = auth_info.lrpcs.vra;
2272         }
2273         break;
2274     default:
2275         FIXME("Unknown keytype 0x%x\n",key->KeyType);
2276     }
2277     return ret;
2278 #elif defined(__APPLE__)
2279     union
2280     {
2281         dk_dvd_report_key_t key;
2282         dk_dvd_read_structure_t disk_key;
2283     } ioc;
2284     union
2285     {
2286         DVDDiscKeyInfo disk_key;
2287         DVDChallengeKeyInfo chal;
2288         DVDKey1Info key1;
2289         DVDTitleKeyInfo title;
2290         DVDAuthenticationSuccessFlagInfo asf;
2291         DVDRegionPlaybackControlInfo rpc;
2292     } desc;
2293     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2294
2295     switch(key->KeyType)
2296     {
2297     case DvdChallengeKey:
2298         ioc.key.format = kDVDKeyFormatChallengeKey;
2299         ioc.key.grantID = (uint8_t)key->SessionId;
2300         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2301         ioc.key.bufferLength = sizeof(desc.chal);
2302         ioc.key.buffer = &desc.chal;
2303         OSWriteBigInt16(desc.chal.dataLength, 0, key->KeyLength);
2304         break;
2305     case DvdBusKey1:
2306         ioc.key.format = kDVDKeyFormatKey1;
2307         ioc.key.grantID = (uint8_t)key->SessionId;
2308         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2309         ioc.key.bufferLength = sizeof(desc.key1);
2310         ioc.key.buffer = &desc.key1;
2311         OSWriteBigInt16(desc.key1.dataLength, 0, key->KeyLength);
2312         break;
2313     case DvdTitleKey:
2314         ioc.key.format = kDVDKeyFormatTitleKey;
2315         ioc.key.grantID = (uint8_t)key->SessionId;
2316         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2317         ioc.key.bufferLength = sizeof(desc.title);
2318         ioc.key.buffer = &desc.title;
2319         ioc.key.address = (uint32_t)(key->Parameters.TitleOffset.QuadPart>>11);
2320         OSWriteBigInt16(desc.title.dataLength, 0, key->KeyLength);
2321         break;
2322     case DvdAsf:
2323         ioc.key.format = kDVDKeyFormatASF;
2324         ioc.key.grantID = (uint8_t)key->SessionId;
2325         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2326         ioc.key.bufferLength = sizeof(desc.asf);
2327         ioc.key.buffer = &desc.asf;
2328         OSWriteBigInt16(desc.asf.dataLength, 0, key->KeyLength);
2329         break;
2330     case DvdGetRpcKey:
2331         ioc.key.format = kDVDKeyFormatRegionState;
2332         ioc.key.grantID = (uint8_t)key->SessionId;
2333         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2334         ioc.key.bufferLength = sizeof(desc.rpc);
2335         ioc.key.buffer = &desc.rpc;
2336         OSWriteBigInt16(desc.rpc.dataLength, 0, key->KeyLength);
2337         break;
2338     case DvdDiskKey:
2339         ioc.disk_key.format = kDVDStructureFormatDiscKeyInfo;
2340         ioc.disk_key.grantID = (uint8_t)key->SessionId;
2341         ioc.disk_key.bufferLength = sizeof(desc.disk_key);
2342         ioc.disk_key.buffer = &desc.disk_key;
2343         break;
2344     case DvdInvalidateAGID:
2345         ioc.key.format = kDVDKeyFormatAGID_Invalidate;
2346         ioc.key.grantID = (uint8_t)key->SessionId;
2347         ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2348         break;
2349     case DvdBusKey2:
2350     case DvdSetRpcKey:
2351         ERR("attempted to read write-only key type 0x%x\n", key->KeyType);
2352         return STATUS_NOT_SUPPORTED;
2353     default:
2354         FIXME("got unknown key type 0x%x\n", key->KeyType);
2355         return STATUS_NOT_SUPPORTED;
2356     }
2357
2358     ret = CDROM_GetStatusCode(ioctl(fd, (key->KeyType == DvdDiskKey ? DKIOCDVDREADSTRUCTURE : DKIOCDVDREPORTKEY), &ioc));
2359
2360     if (ret == STATUS_SUCCESS)
2361     {
2362         switch(key->KeyType)
2363         {
2364         case DvdChallengeKey:
2365             key->KeyLength = OSReadBigInt16(desc.chal.dataLength, 0);
2366             memcpy(key->KeyData, desc.chal.challengeKeyValue, key->KeyLength);
2367             break;
2368         case DvdBusKey1:
2369             key->KeyLength = OSReadBigInt16(desc.key1.dataLength, 0);
2370             memcpy(key->KeyData, desc.key1.key1Value, key->KeyLength);
2371             break;
2372         case DvdTitleKey:
2373             key->KeyLength = OSReadBigInt16(desc.title.dataLength, 0);
2374             memcpy(key->KeyData, desc.title.titleKeyValue, key->KeyLength);
2375             key->KeyFlags = 0;
2376             if (desc.title.CPM)
2377             {
2378                 /*key->KeyFlags |= DVD_COPYRIGHTED;*/
2379                 if (desc.title.CP_SEC) key->KeyFlags |= DVD_SECTOR_PROTECTED;
2380                 /*else key->KeyFlags |= DVD_SECTOR_NOT_PROTECTED;*/
2381 #if 0
2382                 switch (desc.title.CGMS)
2383                 {
2384                 case 0:
2385                     key->KeyFlags |= DVD_CGMS_COPY_PERMITTED;
2386                     break;
2387                 case 2:
2388                     key->KeyFlags |= DVD_CGMS_COPY_ONCE;
2389                     break;
2390                 case 3:
2391                     key->KeyFlags |= DVD_CGMS_NO_COPY;
2392                     break;
2393                 }
2394 #endif
2395             } /*else key->KeyFlags |= DVD_NOT_COPYRIGHTED;*/
2396             break;
2397         case DvdAsf:
2398             key->KeyLength = OSReadBigInt16(desc.title.dataLength, 0);
2399             ((PDVD_ASF)key->KeyData)->SuccessFlag = desc.asf.successFlag;
2400             break;
2401         case DvdGetRpcKey:
2402             key->KeyLength = OSReadBigInt16(desc.rpc.dataLength, 0);
2403             ((PDVD_RPC_KEY)key->KeyData)->UserResetsAvailable =
2404                 desc.rpc.numberUserResets;
2405             ((PDVD_RPC_KEY)key->KeyData)->ManufacturerResetsAvailable =
2406                 desc.rpc.numberVendorResets;
2407             ((PDVD_RPC_KEY)key->KeyData)->TypeCode =
2408                 desc.rpc.typeCode;
2409             ((PDVD_RPC_KEY)key->KeyData)->RegionMask =
2410                 desc.rpc.driveRegion;
2411             ((PDVD_RPC_KEY)key->KeyData)->RpcScheme =
2412                 desc.rpc.rpcScheme;
2413         case DvdDiskKey:
2414             key->KeyLength = OSReadBigInt16(desc.disk_key.dataLength, 0);
2415             memcpy(key->KeyData, desc.disk_key.discKeyStructures, key->KeyLength);
2416             break;
2417         case DvdBusKey2:
2418         case DvdSetRpcKey:
2419         case DvdInvalidateAGID:
2420         default:
2421             /* Silence warning */
2422             ;
2423         }
2424     }
2425     return ret;
2426 #else
2427     FIXME("not supported on this O/S\n");
2428     return STATUS_NOT_SUPPORTED;
2429 #endif
2430 }
2431
2432 /******************************************************************
2433  *              DVD_GetRegion
2434  *
2435  * This IOCTL combines information from both IOCTL_DVD_READ_KEY
2436  * with key type DvdGetRpcKey and IOCTL_DVD_READ_STRUCTURE with
2437  * structure type DvdCopyrightInformation into one structure.
2438  */
2439 static NTSTATUS DVD_GetRegion(int fd, PDVD_REGION region)
2440 {
2441 #if defined(linux)
2442     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2443     dvd_struct dvd;
2444     dvd_authinfo auth_info;
2445
2446     dvd.type = DVD_STRUCT_COPYRIGHT;
2447     dvd.copyright.layer_num = 0;
2448     auth_info.type = DVD_LU_SEND_RPC_STATE;
2449
2450     ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
2451
2452     if (ret == STATUS_SUCCESS)
2453     {
2454         ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd ));
2455
2456         if (ret == STATUS_SUCCESS)
2457         {
2458             region->CopySystem = dvd.copyright.cpst;
2459             region->RegionData = dvd.copyright.rmi;
2460             region->SystemRegion = auth_info.lrpcs.region_mask;
2461             region->ResetCount = auth_info.lrpcs.ucca;
2462         }
2463     }
2464     return ret;
2465 #elif defined(__APPLE__)
2466     dk_dvd_report_key_t key;
2467     dk_dvd_read_structure_t dvd;
2468     DVDRegionPlaybackControlInfo rpc;
2469     DVDCopyrightInfo copy;
2470     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2471
2472     key.format = kDVDKeyFormatRegionState;
2473     key.keyClass = kDVDKeyClassCSS_CPPM_CPRM;
2474     key.bufferLength = sizeof(rpc);
2475     key.buffer = &rpc;
2476     dvd.format = kDVDStructureFormatCopyrightInfo;
2477     dvd.bufferLength = sizeof(copy);
2478     dvd.buffer = &copy;
2479
2480     ret = CDROM_GetStatusCode(ioctl( fd, DKIOCDVDREPORTKEY, &key ));
2481
2482     if (ret == STATUS_SUCCESS)
2483     {
2484         ret = CDROM_GetStatusCode(ioctl( fd, DKIOCDVDREADSTRUCTURE, &dvd ));
2485
2486         if (ret == STATUS_SUCCESS)
2487         {
2488             region->CopySystem = copy.copyrightProtectionSystemType;
2489             region->RegionData = copy.regionMask;
2490             region->SystemRegion = rpc.driveRegion;
2491             region->ResetCount = rpc.numberUserResets;
2492         }
2493     }
2494     return ret;
2495 #else
2496     FIXME("not supported on this O/S\n");
2497     return STATUS_NOT_SUPPORTED;
2498 #endif
2499 }
2500
2501 /******************************************************************
2502  *              DVD_ReadStructure
2503  *
2504  *
2505  */
2506 static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure, PDVD_LAYER_DESCRIPTOR layer)
2507 {
2508 #ifdef DVD_READ_STRUCT
2509     /* dvd_struct is not defined consistently across platforms */
2510     union
2511     {
2512         struct dvd_physical     physical;
2513         struct dvd_copyright    copyright;
2514         struct dvd_disckey      disckey;
2515         struct dvd_bca          bca;
2516         struct dvd_manufact     manufact;
2517     } s;
2518
2519     if (structure->BlockByteOffset.u.HighPart || structure->BlockByteOffset.u.LowPart)
2520         FIXME(": BlockByteOffset is not handled\n");
2521
2522     switch (structure->Format)
2523     {
2524     case DvdPhysicalDescriptor:
2525         s.physical.type = DVD_STRUCT_PHYSICAL;
2526         s.physical.layer_num = structure->LayerNumber;
2527         break;
2528
2529     case DvdCopyrightDescriptor:
2530         s.copyright.type = DVD_STRUCT_COPYRIGHT;
2531         s.copyright.layer_num = structure->LayerNumber;
2532         break;
2533
2534     case DvdDiskKeyDescriptor:
2535         s.disckey.type = DVD_STRUCT_DISCKEY;
2536         s.disckey.agid = structure->SessionId;
2537         break;
2538
2539     case DvdBCADescriptor:
2540         s.bca.type = DVD_STRUCT_BCA;
2541         break;
2542
2543     case DvdManufacturerDescriptor:
2544         s.manufact.type = DVD_STRUCT_MANUFACT;
2545         s.manufact.layer_num = structure->LayerNumber;
2546         break;
2547
2548     case DvdMaxDescriptor: /* This is not a real request, no matter what MSDN says! */
2549     default:
2550         return STATUS_INVALID_PARAMETER;
2551     }
2552
2553     if (ioctl(dev, DVD_READ_STRUCT, &s) < 0)
2554        return STATUS_INVALID_PARAMETER;
2555
2556     switch (structure->Format)
2557     {
2558     case DvdPhysicalDescriptor:
2559         {
2560             internal_dvd_layer_descriptor *p = (internal_dvd_layer_descriptor *) layer;
2561             struct dvd_layer *l = &s.physical.layer[s.physical.layer_num];
2562
2563             p->DataLength[0] = 2;
2564             p->DataLength[1] = 8;
2565             p->Reserved0[0] = 0;
2566             p->Reserved0[1] = 0;
2567             p->BookVersion = l->book_version;
2568             p->BookType = l->book_type;
2569             p->MinimumRate = l->min_rate;
2570             p->DiskSize = l->disc_size;
2571             p->LayerType = l->layer_type;
2572             p->TrackPath = l->track_path;
2573             p->NumberOfLayers = l->nlayers;
2574             p->Reserved1 = 0;
2575             p->TrackDensity = l->track_density;
2576             p->LinearDensity = l->linear_density;
2577             p->StartingDataSector = GET_BE_DWORD(l->start_sector);
2578             p->EndDataSector = GET_BE_DWORD(l->end_sector);
2579             p->EndLayerZeroSector = GET_BE_DWORD(l->end_sector_l0);
2580             p->Reserved5 = 0;
2581             p->BCAFlag = l->bca;
2582             p->Reserved6 = 0;
2583         }
2584         break;
2585
2586     case DvdCopyrightDescriptor:
2587         {
2588             PDVD_COPYRIGHT_DESCRIPTOR p = (PDVD_COPYRIGHT_DESCRIPTOR) layer;
2589
2590             p->CopyrightProtectionType = s.copyright.cpst;
2591             p->RegionManagementInformation = s.copyright.rmi;
2592             p->Reserved = 0;
2593         }
2594         break;
2595
2596     case DvdDiskKeyDescriptor:
2597         {
2598             PDVD_DISK_KEY_DESCRIPTOR p = (PDVD_DISK_KEY_DESCRIPTOR) layer;
2599
2600             memcpy(p->DiskKeyData, s.disckey.value, 2048);
2601         }
2602         break;
2603
2604     case DvdBCADescriptor:
2605         {
2606             PDVD_BCA_DESCRIPTOR p = (PDVD_BCA_DESCRIPTOR) layer;
2607
2608             memcpy(p->BCAInformation, s.bca.value, s.bca.len);
2609         }
2610         break;
2611
2612     case DvdManufacturerDescriptor:
2613         {
2614             PDVD_MANUFACTURER_DESCRIPTOR p = (PDVD_MANUFACTURER_DESCRIPTOR) layer;
2615
2616             memcpy(p->ManufacturingInformation, s.manufact.value, 2048);
2617         }
2618         break;
2619
2620     case DvdMaxDescriptor: /* Suppress warning */
2621         break;
2622     }
2623 #elif defined(__APPLE__)
2624     NTSTATUS ret = STATUS_NOT_SUPPORTED;
2625     dk_dvd_read_structure_t dvdrs;
2626     union
2627     {
2628         DVDPhysicalFormatInfo phys;
2629         DVDCopyrightInfo copy;
2630         DVDDiscKeyInfo disk_key;
2631         DVDManufacturingInfo manf;
2632     } desc;
2633     union
2634     {
2635         PDVD_LAYER_DESCRIPTOR layer;
2636         internal_dvd_layer_descriptor *xlayer;
2637         PDVD_COPYRIGHT_DESCRIPTOR copy;
2638         PDVD_DISK_KEY_DESCRIPTOR disk_key;
2639         PDVD_MANUFACTURER_DESCRIPTOR manf;
2640     } nt_desc;
2641
2642     nt_desc.layer = layer;
2643     RtlZeroMemory(&dvdrs, sizeof(dvdrs));
2644     dvdrs.address = (uint32_t)(structure->BlockByteOffset.QuadPart>>11);
2645     dvdrs.grantID = (uint8_t)structure->SessionId;
2646     dvdrs.layer = structure->LayerNumber;
2647     switch(structure->Format)
2648     {
2649     case DvdPhysicalDescriptor:
2650         dvdrs.format = kDVDStructureFormatPhysicalFormatInfo;
2651         dvdrs.bufferLength = sizeof(desc.phys);
2652         dvdrs.buffer = &desc.phys;
2653         break;
2654
2655     case DvdCopyrightDescriptor:
2656         dvdrs.format = kDVDStructureFormatCopyrightInfo;
2657         dvdrs.bufferLength = sizeof(desc.copy);
2658         dvdrs.buffer = &desc.copy;
2659         break;
2660
2661     case DvdDiskKeyDescriptor:
2662         dvdrs.format = kDVDStructureFormatDiscKeyInfo;
2663         dvdrs.bufferLength = sizeof(desc.disk_key);
2664         dvdrs.buffer = &desc.disk_key;
2665         break;
2666
2667     case DvdBCADescriptor:
2668         FIXME("DvdBCADescriptor NIY\n");
2669         return STATUS_NOT_SUPPORTED;
2670
2671     case DvdManufacturerDescriptor:
2672         dvdrs.format = kDVDStructureFormatManufacturingInfo;
2673         dvdrs.bufferLength = sizeof(desc.manf);
2674         dvdrs.buffer = &desc.manf;
2675         break;
2676
2677     case DvdMaxDescriptor:
2678     default:
2679         FIXME("got unknown structure type 0x%x\n", structure->Format);
2680         return STATUS_NOT_SUPPORTED;
2681     }
2682     ret = CDROM_GetStatusCode(ioctl(dev, DKIOCDVDREADSTRUCTURE, &dvdrs));
2683     if(ret == STATUS_SUCCESS)
2684     {
2685         switch(structure->Format)
2686         {
2687         case DvdPhysicalDescriptor:
2688             nt_desc.xlayer->DataLength[0] = 2;
2689             nt_desc.xlayer->DataLength[1] = 8;
2690             nt_desc.xlayer->Reserved0[0] = 0;
2691             nt_desc.xlayer->Reserved0[1] = 0;
2692             nt_desc.xlayer->BookVersion = desc.phys.partVersion;
2693             nt_desc.xlayer->BookType = desc.phys.bookType;
2694             nt_desc.xlayer->MinimumRate = desc.phys.minimumRate;
2695             nt_desc.xlayer->DiskSize = desc.phys.discSize;
2696             nt_desc.xlayer->LayerType = desc.phys.layerType;
2697             nt_desc.xlayer->TrackPath = desc.phys.trackPath;
2698             nt_desc.xlayer->NumberOfLayers = desc.phys.numberOfLayers;
2699             nt_desc.xlayer->Reserved1 = 0;
2700             nt_desc.xlayer->TrackDensity = desc.phys.trackDensity;
2701             nt_desc.xlayer->LinearDensity = desc.phys.linearDensity;
2702             nt_desc.xlayer->BCAFlag = desc.phys.bcaFlag;
2703             nt_desc.xlayer->StartingDataSector = *(DWORD *)&desc.phys.zero1;
2704             nt_desc.xlayer->EndDataSector = *(DWORD *)&desc.phys.zero2;
2705             nt_desc.xlayer->EndLayerZeroSector = *(DWORD *)&desc.phys.zero3;
2706             nt_desc.xlayer->Reserved5 = 0;
2707             nt_desc.xlayer->Reserved6 = 0;
2708             break;
2709
2710         case DvdCopyrightDescriptor:
2711             nt_desc.copy->CopyrightProtectionType =
2712                 desc.copy.copyrightProtectionSystemType;
2713             nt_desc.copy->RegionManagementInformation =
2714                 desc.copy.regionMask;
2715             nt_desc.copy->Reserved = 0;
2716             break;
2717
2718         case DvdDiskKeyDescriptor:
2719             memcpy(
2720                 nt_desc.disk_key->DiskKeyData,
2721                 desc.disk_key.discKeyStructures,
2722                 2048);
2723             break;
2724
2725         case DvdManufacturerDescriptor:
2726             memcpy(
2727                 nt_desc.manf->ManufacturingInformation,
2728                 desc.manf.discManufacturingInfo,
2729                 2048);
2730             break;
2731
2732         case DvdBCADescriptor:
2733         case DvdMaxDescriptor:
2734         default:
2735             /* Silence warning */
2736             break;
2737         }
2738     }
2739     return ret;
2740 #else
2741     FIXME("\n");
2742 #endif
2743     return STATUS_SUCCESS;
2744
2745 }
2746
2747 /******************************************************************
2748  *        GetInquiryData
2749  *        Implements the IOCTL_GET_INQUIRY_DATA ioctl.
2750  *        Returns Inquiry data for all devices on the specified scsi bus
2751  *        Returns STATUS_BUFFER_TOO_SMALL if the output buffer is too small, 
2752  *        STATUS_INVALID_DEVICE_REQUEST if the given handle isn't to a SCSI device,
2753  *        or STATUS_NOT_SUPPORTED if the OS driver is too old
2754  */
2755 static NTSTATUS GetInquiryData(int fd, PSCSI_ADAPTER_BUS_INFO BufferOut, DWORD OutBufferSize)
2756 {
2757 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
2758     PSCSI_INQUIRY_DATA pInquiryData = NULL;
2759     UCHAR sense_buffer[32];
2760     int iochk, version;
2761     sg_io_hdr_t iocmd;
2762     UCHAR inquiry[INQ_CMD_LEN] = {INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0};
2763
2764     /* Check we have a SCSI device and a supported driver */
2765     if(ioctl(fd, SG_GET_VERSION_NUM, &version) != 0)
2766     {
2767         WARN("IOCTL_SCSI_GET_INQUIRY_DATA sg driver is not loaded\n");
2768         return STATUS_INVALID_DEVICE_REQUEST;
2769     }
2770     if(version < 30000 )
2771         return STATUS_NOT_SUPPORTED;
2772
2773     /* FIXME: Enumerate devices on the bus */
2774     BufferOut->NumberOfBuses = 1;
2775     BufferOut->BusData[0].NumberOfLogicalUnits = 1;
2776     BufferOut->BusData[0].InquiryDataOffset = sizeof(SCSI_ADAPTER_BUS_INFO);
2777
2778     pInquiryData = (PSCSI_INQUIRY_DATA)(BufferOut + 1);
2779
2780     RtlZeroMemory(&iocmd, sizeof(iocmd));
2781     iocmd.interface_id = 'S';
2782     iocmd.cmd_len = sizeof(inquiry);
2783     iocmd.mx_sb_len = sizeof(sense_buffer);
2784     iocmd.dxfer_direction = SG_DXFER_FROM_DEV;
2785     iocmd.dxfer_len = INQ_REPLY_LEN;
2786     iocmd.dxferp = pInquiryData->InquiryData;
2787     iocmd.cmdp = inquiry;
2788     iocmd.sbp = sense_buffer;
2789     iocmd.timeout = 1000;
2790
2791     iochk = ioctl(fd, SG_IO, &iocmd);
2792     if(iochk != 0)
2793         WARN("ioctl SG_IO returned %d, error (%s)\n", iochk, strerror(errno));
2794
2795     CDROM_GetInterfaceInfo(fd, &BufferOut->BusData[0].InitiatorBusId, &pInquiryData->PathId, &pInquiryData->TargetId, &pInquiryData->Lun);
2796     pInquiryData->DeviceClaimed = TRUE;
2797     pInquiryData->InquiryDataLength = INQ_REPLY_LEN;
2798     pInquiryData->NextInquiryDataOffset = 0;
2799     return STATUS_SUCCESS;
2800 #else
2801     FIXME("not supported on this O/S\n");
2802     return STATUS_NOT_SUPPORTED;
2803 #endif
2804 }
2805
2806 /******************************************************************
2807  *              CDROM_DeviceIoControl
2808  *
2809  *
2810  */
2811 NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, 
2812                                HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
2813                                PVOID UserApcContext, 
2814                                PIO_STATUS_BLOCK piosb, 
2815                                ULONG dwIoControlCode,
2816                                LPVOID lpInBuffer, DWORD nInBufferSize,
2817                                LPVOID lpOutBuffer, DWORD nOutBufferSize)
2818 {
2819     DWORD       sz = 0;
2820     NTSTATUS    status = STATUS_SUCCESS;
2821     int fd, needs_close, dev;
2822
2823     TRACE("%p %s %p %d %p %d %p\n",
2824           hDevice, iocodex(dwIoControlCode), lpInBuffer, nInBufferSize,
2825           lpOutBuffer, nOutBufferSize, piosb);
2826
2827     piosb->Information = 0;
2828
2829     if ((status = server_get_unix_fd( hDevice, 0, &fd, &needs_close, NULL, NULL )))
2830     {
2831         if (status == STATUS_BAD_DEVICE_TYPE) return status;  /* no associated fd */
2832         goto error;
2833     }
2834
2835     if ((status = CDROM_Open(fd, &dev)))
2836     {
2837         if (needs_close) close( fd );
2838         goto error;
2839     }
2840
2841 #ifdef __APPLE__
2842     {
2843         char name[100];
2844
2845         /* This is ugly as hell, but Mac OS is unable to do anything from the
2846          * partition fd, it wants an fd for the whole device, and it sometimes
2847          * also requires the device fd to be closed first, so we have to close
2848          * the handle that the caller gave us.
2849          * Also for some reason it wants the fd to be closed before we even
2850          * open the parent if we're trying to eject the disk.
2851          */
2852         if ((status = get_parent_device( fd, name, sizeof(name) ))) goto error;
2853         if (dwIoControlCode == IOCTL_STORAGE_EJECT_MEDIA)
2854             NtClose( hDevice );
2855         if (needs_close) close( fd );
2856         TRACE("opening parent %s\n", name );
2857         if ((fd = open( name, O_RDONLY )) == -1)
2858         {
2859             status = FILE_GetNtStatus();
2860             goto error;
2861         }
2862         needs_close = 1;
2863     }
2864 #endif
2865
2866     switch (dwIoControlCode)
2867     {
2868     case IOCTL_CDROM_CHECK_VERIFY:
2869     case IOCTL_DISK_CHECK_VERIFY:
2870     case IOCTL_STORAGE_CHECK_VERIFY:
2871     case IOCTL_STORAGE_CHECK_VERIFY2:
2872         sz = 0;
2873         CDROM_ClearCacheEntry(dev);
2874         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2875             status = STATUS_INVALID_PARAMETER;
2876         else status = CDROM_Verify(dev, fd);
2877         break;
2878
2879 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
2880
2881 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
2882 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
2883
2884     case IOCTL_STORAGE_LOAD_MEDIA:
2885     case IOCTL_CDROM_LOAD_MEDIA:
2886         sz = 0;
2887         CDROM_ClearCacheEntry(dev);
2888         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2889             status = STATUS_INVALID_PARAMETER;
2890         else status = CDROM_SetTray(fd, FALSE);
2891         break;
2892      case IOCTL_STORAGE_EJECT_MEDIA:
2893         sz = 0;
2894         CDROM_ClearCacheEntry(dev);
2895         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2896             status = STATUS_INVALID_PARAMETER;
2897         else
2898             status = CDROM_SetTray(fd, TRUE);
2899         break;
2900
2901     case IOCTL_CDROM_MEDIA_REMOVAL:
2902     case IOCTL_DISK_MEDIA_REMOVAL:
2903     case IOCTL_STORAGE_MEDIA_REMOVAL:
2904     case IOCTL_STORAGE_EJECTION_CONTROL:
2905         /* FIXME the last ioctl:s is not the same as the two others...
2906          * lockcount/owner should be handled */
2907         sz = 0;
2908         CDROM_ClearCacheEntry(dev);
2909         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2910         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL;
2911         else status = CDROM_ControlEjection(fd, lpInBuffer);
2912         break;
2913
2914     case IOCTL_STORAGE_GET_MEDIA_TYPES:
2915     case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:
2916         sz = sizeof(GET_MEDIA_TYPES);
2917         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2918         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2919         else status = CDROM_GetMediaType(dev, lpOutBuffer);
2920         break;
2921
2922     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
2923         sz = sizeof(STORAGE_DEVICE_NUMBER);
2924         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2925         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2926         else status = CDROM_GetDeviceNumber(dev, lpOutBuffer);
2927         break;
2928
2929     case IOCTL_STORAGE_RESET_DEVICE:
2930         sz = 0;
2931         CDROM_ClearCacheEntry(dev);
2932         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2933             status = STATUS_INVALID_PARAMETER;
2934         else status = CDROM_ResetAudio(fd);
2935         break;
2936
2937     case IOCTL_CDROM_GET_CONTROL:
2938         sz = sizeof(CDROM_AUDIO_CONTROL);
2939         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2940         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2941         else status = CDROM_GetControl(dev, fd, lpOutBuffer);
2942         break;
2943
2944     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
2945         sz = sizeof(DISK_GEOMETRY);
2946         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2947         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2948         else status = CDROM_GetDriveGeometry(dev, fd, lpOutBuffer);
2949         break;
2950
2951     case IOCTL_CDROM_DISK_TYPE:
2952         sz = sizeof(CDROM_DISK_DATA);
2953         /* CDROM_ClearCacheEntry(dev); */
2954         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2955         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2956         else status = CDROM_GetDiskData(dev, fd, lpOutBuffer);
2957         break;
2958
2959 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
2960
2961     case IOCTL_CDROM_READ_Q_CHANNEL:
2962         sz = sizeof(SUB_Q_CHANNEL_DATA);
2963         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
2964             status = STATUS_INVALID_PARAMETER;
2965         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2966         else status = CDROM_ReadQChannel(dev, fd, lpInBuffer, lpOutBuffer);
2967         break;
2968
2969     case IOCTL_CDROM_READ_TOC:
2970         sz = sizeof(CDROM_TOC);
2971         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2972         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2973         else status = CDROM_ReadTOC(dev, fd, lpOutBuffer);
2974         break;
2975
2976 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
2977
2978     case IOCTL_CDROM_PAUSE_AUDIO:
2979         sz = 0;
2980         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2981             status = STATUS_INVALID_PARAMETER;
2982         else status = CDROM_PauseAudio(fd);
2983         break;
2984     case IOCTL_CDROM_PLAY_AUDIO_MSF:
2985         sz = 0;
2986         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2987         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
2988         else status = CDROM_PlayAudioMSF(fd, lpInBuffer);
2989         break;
2990     case IOCTL_CDROM_RESUME_AUDIO:
2991         sz = 0;
2992         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2993             status = STATUS_INVALID_PARAMETER;
2994         else status = CDROM_ResumeAudio(fd);
2995         break;
2996     case IOCTL_CDROM_SEEK_AUDIO_MSF:
2997         sz = 0;
2998         if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2999         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
3000         else status = CDROM_SeekAudioMSF(dev, fd, lpInBuffer);
3001         break;
3002     case IOCTL_CDROM_STOP_AUDIO:
3003         sz = 0;
3004         CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
3005         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
3006             status = STATUS_INVALID_PARAMETER;
3007         else status = CDROM_StopAudio(fd);
3008         break;
3009     case IOCTL_CDROM_GET_VOLUME:
3010         sz = sizeof(VOLUME_CONTROL);
3011         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
3012         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
3013         else status = CDROM_GetVolume(fd, lpOutBuffer);
3014         break;
3015     case IOCTL_CDROM_SET_VOLUME:
3016         sz = 0;
3017         CDROM_ClearCacheEntry(dev);
3018         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
3019             status = STATUS_INVALID_PARAMETER;
3020         else status = CDROM_SetVolume(fd, lpInBuffer);
3021         break;
3022     case IOCTL_CDROM_RAW_READ:
3023         sz = 0;
3024         if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER;
3025         else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL;
3026         else status = CDROM_RawRead(fd, lpInBuffer, lpOutBuffer,
3027                                     nOutBufferSize, &sz);
3028         break;
3029     case IOCTL_SCSI_GET_ADDRESS:
3030         sz = sizeof(SCSI_ADDRESS);
3031         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
3032         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
3033         else status = CDROM_GetAddress(fd, lpOutBuffer);
3034         break;
3035     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
3036         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
3037         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
3038         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL;
3039         else status = CDROM_ScsiPassThroughDirect(fd, lpOutBuffer);
3040         break;
3041     case IOCTL_SCSI_PASS_THROUGH:
3042         sz = sizeof(SCSI_PASS_THROUGH);
3043         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
3044         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL;
3045         else status = CDROM_ScsiPassThrough(fd, lpOutBuffer);
3046         break;
3047     case IOCTL_SCSI_GET_CAPABILITIES:
3048         sz = sizeof(IO_SCSI_CAPABILITIES);
3049         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
3050         else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL;
3051         else status = CDROM_ScsiGetCaps(fd, lpOutBuffer);
3052         break;
3053     case IOCTL_DVD_START_SESSION:
3054         sz = sizeof(DVD_SESSION_ID);
3055         if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
3056         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
3057         else
3058         {
3059             TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
3060                   *(PDVD_SESSION_ID)lpOutBuffer);
3061             status = DVD_StartSession(fd, lpInBuffer, lpOutBuffer);
3062             TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
3063                   *(PDVD_SESSION_ID)lpOutBuffer);
3064         }
3065         break;
3066     case IOCTL_DVD_END_SESSION:
3067         sz = sizeof(DVD_SESSION_ID);
3068         if ((lpInBuffer == NULL) ||  (nInBufferSize < sz))status = STATUS_INVALID_PARAMETER;
3069         else status = DVD_EndSession(fd, lpInBuffer);
3070         break;
3071     case IOCTL_DVD_SEND_KEY:
3072         sz = 0;
3073         if (!lpInBuffer ||
3074             (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
3075             status = STATUS_INVALID_PARAMETER;
3076         else
3077         {
3078             TRACE("doing DVD_SendKey\n");
3079             status = DVD_SendKey(fd, lpInBuffer);
3080         }
3081         break;
3082     case IOCTL_DVD_READ_KEY:
3083         if (!lpInBuffer ||
3084             (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
3085             status = STATUS_INVALID_PARAMETER;
3086         else if (lpInBuffer !=lpOutBuffer) status = STATUS_BUFFER_TOO_SMALL;
3087         else
3088         {
3089             TRACE("doing DVD_READ_KEY\n");
3090             sz = ((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength;
3091             status = DVD_ReadKey(fd, lpInBuffer);
3092         }
3093         break;
3094     case IOCTL_DVD_GET_REGION:
3095         sz = sizeof(DVD_REGION);
3096         if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
3097         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
3098         TRACE("doing DVD_Get_REGION\n");
3099         status = DVD_GetRegion(fd, lpOutBuffer);
3100         break;
3101     case IOCTL_DVD_READ_STRUCTURE:
3102         sz = sizeof(DVD_LAYER_DESCRIPTOR);
3103         if (lpInBuffer == NULL || nInBufferSize != sizeof(DVD_READ_STRUCTURE)) status = STATUS_INVALID_PARAMETER;
3104         else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
3105         else
3106         {
3107             TRACE("doing DVD_READ_STRUCTURE\n");
3108             status = DVD_ReadStructure(fd, lpInBuffer, lpOutBuffer);
3109         }
3110         break;
3111
3112     case IOCTL_SCSI_GET_INQUIRY_DATA:
3113         sz = INQ_REPLY_LEN;
3114         status = GetInquiryData(fd, lpOutBuffer, nOutBufferSize);
3115         break;
3116
3117     default:
3118         if (needs_close) close( fd );
3119         return STATUS_NOT_SUPPORTED;
3120     }
3121     if (needs_close) close( fd );
3122  error:
3123     piosb->u.Status = status;
3124     piosb->Information = sz;
3125     if (hEvent) NtSetEvent(hEvent, NULL);
3126     return status;
3127 }