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