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