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