Implementation of RtlGetNtVersionNumbers.
[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 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
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32
33 #ifdef HAVE_SYS_IOCTL_H
34 #include <sys/ioctl.h>
35 #endif
36 #ifdef HAVE_SCSI_SG_H
37 # include <scsi/sg.h>
38 #endif
39 #ifdef HAVE_LINUX_MAJOR_H
40 # include <linux/major.h>
41 #endif
42 #ifdef HAVE_LINUX_HDREG_H
43 # include <linux/hdreg.h>
44 #endif
45 #ifdef HAVE_LINUX_PARAM_H
46 # include <linux/param.h>
47 #endif
48 #ifdef HAVE_LINUX_CDROM_H
49 # include <linux/cdrom.h>
50 #endif
51 #ifdef HAVE_LINUX_UCDROM_H
52 # include <linux/ucdrom.h>
53 #endif
54 #ifdef HAVE_SYS_CDIO_H
55 # include <sys/cdio.h>
56 #endif
57
58 #include "ntddk.h"
59 #include "winioctl.h"
60 #include "ntddstor.h"
61 #include "ntddcdrm.h"
62 #include "ntddscsi.h"
63 #include "drive.h"
64 #include "file.h"
65 #include "wine/debug.h"
66
67 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
68
69 #ifdef linux
70
71 # ifndef IDE6_MAJOR
72 #  define IDE6_MAJOR 88
73 # endif
74 # ifndef IDE7_MAJOR
75 #  define IDE7_MAJOR 89
76 # endif
77
78 # ifdef CDROM_SEND_PACKET
79 /* structure for CDROM_PACKET_COMMAND ioctl */
80 /* not all Linux versions have all the fields, so we define the
81  * structure ourselves to make sure */
82 struct linux_cdrom_generic_command
83 {
84     unsigned char          cmd[CDROM_PACKET_SIZE];
85     unsigned char         *buffer;
86     unsigned int           buflen;
87     int                    stat;
88     struct request_sense  *sense;
89     unsigned char          data_direction;
90     int                    quiet;
91     int                    timeout;
92     void                  *reserved[1];
93 };
94 # endif  /* CDROM_SEND_PACKET */
95
96 #endif  /* linux */
97
98 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
99  * this should be removed when a proper device interface is implemented
100  */
101 struct cdrom_cache {
102     int fd;
103     int count;
104 };
105 static struct cdrom_cache cdrom_cache[26];
106
107 /******************************************************************
108  *              CDROM_GetIdeInterface
109  *
110  * Determines the ide interface (the number after the ide), and the
111  * number of the device on that interface for ide cdroms.
112  * Returns false if the info could not be get
113  *
114  * NOTE: this function is used in CDROM_InitRegistry and CDROM_GetAddress
115  */
116 static int CDROM_GetIdeInterface(int dev, int* iface, int* device)
117 {
118 #if defined(linux)
119     {
120         struct stat st;
121 #ifdef SG_EMULATED_HOST
122         if (ioctl(dev, SG_EMULATED_HOST) != -1) {
123             FIXME("not implemented for true scsi drives\n");
124             return 0;
125         }
126 #endif
127         if ( fstat(dev, &st) == -1 || ! S_ISBLK(st.st_mode)) {
128             FIXME("cdrom not a block device!!!\n");
129             return 0;
130         }
131         switch (major(st.st_rdev)) {
132             case IDE0_MAJOR: *iface = 0; break;
133             case IDE1_MAJOR: *iface = 1; break;
134             case IDE2_MAJOR: *iface = 2; break;
135             case IDE3_MAJOR: *iface = 3; break;
136             case IDE4_MAJOR: *iface = 4; break;
137             case IDE5_MAJOR: *iface = 5; break;
138             case IDE6_MAJOR: *iface = 6; break;
139             case IDE7_MAJOR: *iface = 7; break;
140             case SCSI_CDROM_MAJOR: *iface = 11; break;
141             default:
142                 FIXME("CD-ROM device with major ID %d not supported\n", major(st.st_rdev));
143                 break;
144         }
145         *device = (minor(st.st_rdev) == 63 ? 1 : 0);
146         return 1;
147     }
148 #elif defined(__FreeBSD__) || defined(__NetBSD__)
149     FIXME("not implemented for BSD\n");
150     return 0;
151 #else
152     FIXME("not implemented for nonlinux\n");
153     return 0;
154 #endif
155 }
156
157
158 /******************************************************************
159  *              CDROM_InitRegistry
160  *
161  * Initializes registry to contain scsi info about the cdrom in NT.
162  * All devices (even not real scsi ones) have this info in NT.
163  * TODO: for now it only works for non scsi devices
164  * NOTE: programs usually read these registry entries after sending the
165  *       IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
166  */
167 void CDROM_InitRegistry(int dev)
168 {
169     int portnum, targetid;
170     int dma;
171     OBJECT_ATTRIBUTES attr;
172     UNICODE_STRING nameW;
173     WCHAR dataW[50];
174     DWORD lenW;
175     char buffer[40];
176     DWORD value;
177     const char *data;
178     HKEY scsiKey;
179     HKEY portKey;
180     HKEY busKey;
181     HKEY targetKey;
182     DWORD disp;
183
184     attr.Length = sizeof(attr);
185     attr.RootDirectory = HKEY_LOCAL_MACHINE;
186     attr.ObjectName = &nameW;
187     attr.Attributes = 0;
188     attr.SecurityDescriptor = NULL;
189     attr.SecurityQualityOfService = NULL;
190
191     if ( ! CDROM_GetIdeInterface(dev, &portnum, &targetid))
192         return;
193
194     /* Ensure there is Scsi key */
195     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "HARDWARE\\DEVICEMAP\\Scsi" ) ||
196         NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
197                      NULL, REG_OPTION_VOLATILE, &disp ))
198     {
199         ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
200         return;
201     }
202     RtlFreeUnicodeString( &nameW );
203
204     snprintf(buffer,40,"Scsi Port %d",portnum);
205     attr.RootDirectory = scsiKey;
206     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
207         NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
208                      NULL, REG_OPTION_VOLATILE, &disp ))
209     {
210         ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
211         return;
212     }
213     RtlFreeUnicodeString( &nameW );
214
215     RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
216     data = "atapi";
217     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
218     NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
219     RtlFreeUnicodeString( &nameW );
220     value = 10;
221     RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
222     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
223     RtlFreeUnicodeString( &nameW );
224     value = 0;
225 #ifdef HDIO_GET_DMA
226     if (ioctl(dev,HDIO_GET_DMA, &dma) != -1) {
227         value = dma;
228         TRACE("setting dma to %lx\n", value);
229     }
230 #endif
231     RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
232     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
233     RtlFreeUnicodeString( &nameW );
234
235     attr.RootDirectory = portKey;
236     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Scsi Bus 0" ) ||
237         NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
238                      NULL, REG_OPTION_VOLATILE, &disp ))
239     {
240         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
241         return;
242     }
243     RtlFreeUnicodeString( &nameW );
244
245     attr.RootDirectory = busKey;
246     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
247         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
248                      NULL, REG_OPTION_VOLATILE, &disp ))
249     {
250         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
251         return;
252     }
253     RtlFreeUnicodeString( &nameW );
254     NtClose( targetKey );
255
256     snprintf(buffer,40,"Target Id %d", targetid);
257     attr.RootDirectory = busKey;
258     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
259         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
260                      NULL, REG_OPTION_VOLATILE, &disp ))
261     {
262         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
263         return;
264     }
265     RtlFreeUnicodeString( &nameW );
266
267     RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
268     data = "CdRomPeripheral";
269     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
270     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
271     RtlFreeUnicodeString( &nameW );
272     /* FIXME - maybe read the real identifier?? */
273     RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
274     data = "Wine CDROM";
275     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
276     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
277     RtlFreeUnicodeString( &nameW );
278     /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */
279     RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
280     data = "Cdrom0";
281     RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
282     NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
283     RtlFreeUnicodeString( &nameW );
284
285     NtClose( targetKey );
286     NtClose( busKey );
287     NtClose( portKey );
288     NtClose( scsiKey );
289 }
290
291
292 /******************************************************************
293  *              CDROM_Open
294  *
295  *
296  */
297 static int CDROM_Open(HANDLE hDevice, DWORD clientID)
298 {
299     int dev = LOWORD(clientID);
300
301     if (dev >= 26) return -1;
302
303     if (!cdrom_cache[dev].count)
304     {
305         char root[4];
306         const char *device;
307
308         strcpy(root, "A:\\");
309         root[0] += dev;
310         if (GetDriveTypeA(root) != DRIVE_CDROM) return -1;
311         if (!(device = DRIVE_GetDevice(dev))) return -1;
312         cdrom_cache[dev].fd = open(device, O_RDONLY|O_NONBLOCK);
313         if (cdrom_cache[dev].fd == -1)
314         {
315             FIXME("Can't open configured CD-ROM drive at %s (device %s): %s\n", root, DRIVE_GetDevice(dev), strerror(errno));
316             return -1;
317         }
318     }
319     cdrom_cache[dev].count++;
320     return cdrom_cache[dev].fd;
321 }
322
323 /******************************************************************
324  *              CDROM_Close
325  *
326  *
327  */
328 static void CDROM_Close(DWORD clientID, int fd)
329 {
330     int dev = LOWORD(clientID);
331
332     if (dev >= 26 || fd != cdrom_cache[dev].fd) FIXME("how come\n");
333     if (--cdrom_cache[dev].count == 0)
334         close(cdrom_cache[dev].fd);
335 }
336
337 /******************************************************************
338  *              CDROM_GetStatusCode
339  *
340  *
341  */
342 static DWORD CDROM_GetStatusCode(int io)
343 {
344     if (io == 0) return 0;
345     switch (errno)
346     {
347     case EIO:
348 #ifdef ENOMEDIUM
349     case ENOMEDIUM:
350 #endif
351             return STATUS_NO_MEDIA_IN_DEVICE;
352     case EPERM:
353             return STATUS_ACCESS_DENIED;
354     }
355     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
356     return STATUS_IO_DEVICE_ERROR;
357 }
358
359 static DWORD CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
360 {
361     cac->LbaFormat = 0; /* FIXME */
362     cac->LogicalBlocksPerSecond = 1; /* FIXME */
363     return  STATUS_NOT_SUPPORTED;
364 }
365
366 static DWORD CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
367 {
368     return STATUS_NOT_SUPPORTED;
369 }
370
371 static DWORD CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
372 {
373 #if 0
374     dg->Cylinders.s.LowPart = 1; /* FIXME */
375     dg->Cylinders.s.HighPart = 0; /* FIXME */
376     dg->MediaType = 1; /* FIXME */
377     dg->TracksPerCylinder = 1; /* FIXME */
378     dg->SectorsPerTrack = 1; /* FIXME */
379     dg->BytesPerSector= 1; /* FIXME */
380 #endif
381     return STATUS_NOT_SUPPORTED;
382 }
383
384 /**************************************************************************
385  *                              CDROM_Reset                     [internal]
386  */
387 static DWORD CDROM_ResetAudio(int dev)
388 {
389 #if defined(linux)
390     return CDROM_GetStatusCode(ioctl(dev, CDROMRESET));
391 #elif defined(__FreeBSD__) || defined(__NetBSD__)
392     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESET, NULL));
393 #else
394     return STATUS_NOT_SUPPORTED;
395 #endif
396 }
397
398 /******************************************************************
399  *              CDROM_SetTray
400  *
401  *
402  */
403 static DWORD CDROM_SetTray(int dev, BOOL doEject)
404 {
405 #if defined(linux)
406     return CDROM_GetStatusCode(ioctl(dev, doEject ? CDROMEJECT : CDROMCLOSETRAY));
407 #elif defined(__FreeBSD__) || defined(__NetBSD__)
408     return CDROM_GetStatusCode((ioctl(dev, CDIOCALLOW, NULL)) ||
409                                (ioctl(dev, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
410                                (ioctl(dev, CDIOCPREVENT, NULL)));
411 #else
412     return STATUS_NOT_SUPPORTED;
413 #endif
414 }
415
416 /******************************************************************
417  *              CDROM_ControlEjection
418  *
419  *
420  */
421 static DWORD CDROM_ControlEjection(int dev, const PREVENT_MEDIA_REMOVAL* rmv)
422 {
423 #if defined(linux)
424     return CDROM_GetStatusCode(ioctl(dev, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
425 #elif defined(__FreeBSD__) || defined(__NetBSD__)
426     return CDROM_GetStatusCode(ioctl(dev, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
427 #else
428     return STATUS_NOT_SUPPORTED;
429 #endif
430 }
431
432 /******************************************************************
433  *              CDROM_ReadTOC
434  *
435  *
436  */
437 static DWORD CDROM_ReadTOC(int dev, CDROM_TOC* toc)
438 {
439     DWORD       ret = STATUS_NOT_SUPPORTED;
440
441 #if defined(linux)
442     int                         i, io = -1;
443     struct cdrom_tochdr         hdr;
444     struct cdrom_tocentry       entry;
445
446     io = ioctl(dev, CDROMREADTOCHDR, &hdr);
447     if (io == -1)
448     {
449         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
450         goto end;
451     }
452     toc->FirstTrack = hdr.cdth_trk0;
453     toc->LastTrack  = hdr.cdth_trk1;
454
455     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
456
457     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
458     {
459         if (i == toc->LastTrack + 1)
460         {
461             entry.cdte_track = CDROM_LEADOUT;
462         } else {
463             entry.cdte_track = i;
464         }
465         entry.cdte_format = CDROM_MSF;
466         io = ioctl(dev, CDROMREADTOCENTRY, &entry);
467         if (io == -1) {
468             WARN("error read entry (%s)\n", strerror(errno));
469             goto end;
470         }
471         toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
472         toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
473         /* marking last track with leadout value as index */
474         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
475         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
476         toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
477         toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
478         toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
479     }
480 end:
481     ret = CDROM_GetStatusCode(io);
482 #elif defined(__FreeBSD__) || defined(__NetBSD__)
483     int                         i, io = -1;
484     struct ioc_toc_header       hdr;
485     struct ioc_read_toc_entry   entry;
486     struct cd_toc_entry         toc_buffer;
487
488     io = ioctl(dev, CDIOREADTOCHEADER, &hdr);
489     if (io == -1)
490     {
491         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
492         goto end;
493     }
494     toc->FirstTrack = hdr.starting_track;
495     toc->LastTrack  = hdr.ending_track;
496
497     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
498
499     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
500     {
501         if (i == toc->LastTrack + 1)
502         {
503 #define LEADOUT 0xaa
504             entry.starting_track = LEADOUT;
505         } else {
506             entry.starting_track = i;
507         }
508         memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
509         entry.address_format = CD_MSF_FORMAT;
510         entry.data_len = sizeof(toc_buffer);
511         entry.data = &toc_buffer;
512         io = ioctl(dev, CDIOREADTOCENTRYS, &entry);
513         if (io == -1) {
514             WARN("error read entry (%s)\n", strerror(errno));
515             goto end;
516         }
517         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
518         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
519         /* marking last track with leadout value as index */
520         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
521         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
522         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
523         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
524         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
525     }
526 end:
527     ret = CDROM_GetStatusCode(io);
528 #else
529     ret = STATUS_NOT_SUPPORTED;
530 #endif
531     return ret;
532 }
533
534 /******************************************************************
535  *              CDROM_GetDiskData
536  *
537  *
538  */
539 static DWORD CDROM_GetDiskData(int dev, CDROM_DISK_DATA* data)
540 {
541     CDROM_TOC   toc;
542     DWORD       ret;
543     int         i;
544
545     if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
546     data->DiskData = 0;
547     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
548         if (toc.TrackData[i].Control & 0x04)
549             data->DiskData |= CDROM_DISK_DATA_TRACK;
550         else
551             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
552     }
553     return 0;
554 }
555
556 /******************************************************************
557  *              CDROM_ReadQChannel
558  *
559  *
560  */
561 static DWORD CDROM_ReadQChannel(int dev, const CDROM_SUB_Q_DATA_FORMAT* fmt,
562                                 SUB_Q_CHANNEL_DATA* data)
563 {
564     DWORD               ret = STATUS_NOT_SUPPORTED;
565 #ifdef linux
566     unsigned            size;
567     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
568     int                 io;
569     struct cdrom_subchnl        sc;
570     sc.cdsc_format = CDROM_MSF;
571
572     io = ioctl(dev, CDROMSUBCHNL, &sc);
573     if (io == -1)
574     {
575         TRACE("opened or no_media (%s)!\n", strerror(errno));
576         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
577         goto end;
578     }
579
580     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
581
582     switch (sc.cdsc_audiostatus) {
583     case CDROM_AUDIO_INVALID:
584         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
585         break;
586     case CDROM_AUDIO_NO_STATUS:
587         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
588         break;
589     case CDROM_AUDIO_PLAY:
590         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
591         break;
592     case CDROM_AUDIO_PAUSED:
593         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
594         break;
595     case CDROM_AUDIO_COMPLETED:
596         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
597         break;
598     case CDROM_AUDIO_ERROR:
599         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
600         break;
601     default:
602         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
603         break;
604     }
605     switch (fmt->Format)
606     {
607     case IOCTL_CDROM_CURRENT_POSITION:
608         size = sizeof(SUB_Q_CURRENT_POSITION);
609         data->CurrentPosition.FormatCode = sc.cdsc_format;
610         data->CurrentPosition.Control = sc.cdsc_ctrl;
611         data->CurrentPosition.ADR = sc.cdsc_adr;
612         data->CurrentPosition.TrackNumber = sc.cdsc_trk;
613         data->CurrentPosition.IndexNumber = sc.cdsc_ind;
614
615         data->CurrentPosition.AbsoluteAddress[0] = 0;
616         data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
617         data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
618         data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
619
620         data->CurrentPosition.TrackRelativeAddress[0] = 0;
621         data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
622         data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
623         data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
624         break;
625     case IOCTL_CDROM_MEDIA_CATALOG:
626         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
627         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
628         {
629             struct cdrom_mcn mcn;
630             if ((io = ioctl(dev, CDROM_GET_MCN, &mcn)) == -1) goto end;
631
632             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
633             data->MediaCatalog.Mcval = 0; /* FIXME */
634             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
635             data->MediaCatalog.MediaCatalog[14] = 0;
636         }
637         break;
638     case IOCTL_CDROM_TRACK_ISRC:
639         size = sizeof(SUB_Q_CURRENT_POSITION);
640         FIXME("TrackIsrc: NIY on linux\n");
641         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
642         data->TrackIsrc.Tcval = 0;
643         io = 0;
644         break;
645     }
646
647  end:
648     ret = CDROM_GetStatusCode(io);
649 #elif defined(__FreeBSD__) || defined(__NetBSD__)
650     unsigned            size;
651     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
652     int                 io;
653     struct ioc_read_subchannel  read_sc;
654     struct cd_sub_channel_info  sc;
655
656     read_sc.address_format = CD_MSF_FORMAT;
657     read_sc.track          = 0;
658     read_sc.data_len       = sizeof(sc);
659     read_sc.data           = &sc;
660     switch (fmt->Format)
661     {
662     case IOCTL_CDROM_CURRENT_POSITION:
663         read_sc.data_format    = CD_CURRENT_POSITION;
664         break;
665     case IOCTL_CDROM_MEDIA_CATALOG:
666         read_sc.data_format    = CD_MEDIA_CATALOG;
667         break;
668     case IOCTL_CDROM_TRACK_ISRC:
669         read_sc.data_format    = CD_TRACK_INFO;
670         sc.what.track_info.track_number = data->TrackIsrc.Track;
671         break;
672     }
673     io = ioctl(dev, CDIOCREADSUBCHANNEL, &read_sc);
674     if (io == -1)
675     {
676         TRACE("opened or no_media (%s)!\n", strerror(errno));
677         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
678         goto end;
679     }
680
681     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
682
683     switch (sc.header.audio_status) {
684     case CD_AS_AUDIO_INVALID:
685         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
686         break;
687     case CD_AS_NO_STATUS:
688         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
689         break;
690     case CD_AS_PLAY_IN_PROGRESS:
691         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
692         break;
693     case CD_AS_PLAY_PAUSED:
694         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
695         break;
696     case CD_AS_PLAY_COMPLETED:
697         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
698         break;
699     case CD_AS_PLAY_ERROR:
700         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
701         break;
702     default:
703         TRACE("status=%02X !\n", sc.header.audio_status);
704     }
705     switch (fmt->Format)
706     {
707     case IOCTL_CDROM_CURRENT_POSITION:
708         size = sizeof(SUB_Q_CURRENT_POSITION);
709         data->CurrentPosition.FormatCode = sc.what.position.data_format;
710         data->CurrentPosition.Control = sc.what.position.control;
711         data->CurrentPosition.ADR = sc.what.position.addr_type;
712         data->CurrentPosition.TrackNumber = sc.what.position.track_number;
713         data->CurrentPosition.IndexNumber = sc.what.position.index_number;
714
715         data->CurrentPosition.AbsoluteAddress[0] = 0;
716         data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
717         data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
718         data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
719         data->CurrentPosition.TrackRelativeAddress[0] = 0;
720         data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
721         data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
722         data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
723         break;
724     case IOCTL_CDROM_MEDIA_CATALOG:
725         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
726         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
727         data->MediaCatalog.FormatCode = sc.what.media_catalog.data_format;
728         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
729         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
730         break;
731     case IOCTL_CDROM_TRACK_ISRC:
732         size = sizeof(SUB_Q_CURRENT_POSITION);
733         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
734         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
735         break;
736     }
737
738  end:
739     ret = CDROM_GetStatusCode(io);
740 #endif
741     return ret;
742 }
743
744 /******************************************************************
745  *              CDROM_Verify
746  *
747  *
748  */
749 static DWORD CDROM_Verify(int dev)
750 {
751     /* quick implementation */
752     CDROM_SUB_Q_DATA_FORMAT     fmt;
753     SUB_Q_CHANNEL_DATA          data;
754
755     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
756     return CDROM_ReadQChannel(dev, &fmt, &data) ? 0 : 1;
757 }
758
759 /******************************************************************
760  *              CDROM_PlayAudioMSF
761  *
762  *
763  */
764 static DWORD CDROM_PlayAudioMSF(int dev, const CDROM_PLAY_AUDIO_MSF* audio_msf)
765 {
766     DWORD       ret = STATUS_NOT_SUPPORTED;
767 #ifdef linux
768     struct      cdrom_msf       msf;
769     int         io;
770
771     msf.cdmsf_min0   = audio_msf->StartingM;
772     msf.cdmsf_sec0   = audio_msf->StartingS;
773     msf.cdmsf_frame0 = audio_msf->StartingF;
774     msf.cdmsf_min1   = audio_msf->EndingM;
775     msf.cdmsf_sec1   = audio_msf->EndingS;
776     msf.cdmsf_frame1 = audio_msf->EndingF;
777
778     io = ioctl(dev, CDROMSTART);
779     if (io == -1)
780     {
781         WARN("motor doesn't start !\n");
782         goto end;
783     }
784     io = ioctl(dev, CDROMPLAYMSF, &msf);
785     if (io == -1)
786     {
787         WARN("device doesn't play !\n");
788         goto end;
789     }
790     TRACE("msf = %d:%d:%d %d:%d:%d\n",
791           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
792           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
793  end:
794     ret = CDROM_GetStatusCode(io);
795 #elif defined(__FreeBSD__) || defined(__NetBSD__)
796     struct      ioc_play_msf    msf;
797     int         io;
798
799     msf.start_m      = audio_msf->StartingM;
800     msf.start_s      = audio_msf->StartingS;
801     msf.start_f      = audio_msf->StartingF;
802     msf.end_m        = audio_msf->EndingM;
803     msf.end_s        = audio_msf->EndingS;
804     msf.end_f        = audio_msf->EndingF;
805
806     io = ioctl(dev, CDIOCSTART, NULL);
807     if (io == -1)
808     {
809         WARN("motor doesn't start !\n");
810         goto end;
811     }
812     io = ioctl(dev, CDIOCPLAYMSF, &msf);
813     if (io == -1)
814     {
815         WARN("device doesn't play !\n");
816         goto end;
817     }
818     TRACE("msf = %d:%d:%d %d:%d:%d\n",
819           msf.start_m, msf.start_s, msf.start_f,
820           msf.end_m,   msf.end_s,   msf.end_f);
821 end:
822     ret = CDROM_GetStatusCode(io);
823 #endif
824     return ret;
825 }
826
827 /******************************************************************
828  *              CDROM_SeekAudioMSF
829  *
830  *
831  */
832 static DWORD CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
833 {
834 #if defined(linux)
835     struct cdrom_msf0   msf;
836     msf.minute = audio_msf->M;
837     msf.second = audio_msf->S;
838     msf.frame  = audio_msf->F;
839
840     return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
841 #elif defined(__FreeBSD__) || defined(__NetBSD__)
842     FIXME("Could a BSD expert implement the seek function ?\n");
843     return STATUS_NOT_SUPPORTED;
844 #else
845     return STATUS_NOT_SUPPORTED;
846 #endif
847 }
848
849 /******************************************************************
850  *              CDROM_PauseAudio
851  *
852  *
853  */
854 static DWORD CDROM_PauseAudio(int dev)
855 {
856 #if defined(linux)
857     return CDROM_GetStatusCode(ioctl(dev, CDROMPAUSE));
858 #elif defined(__FreeBSD__) || defined(__NetBSD__)
859     return CDROM_GetStatusCode(ioctl(dev, CDIOCPAUSE, NULL));
860 #else
861     return STATUS_NOT_SUPPORTED;
862 #endif
863 }
864
865 /******************************************************************
866  *              CDROM_ResumeAudio
867  *
868  *
869  */
870 static DWORD CDROM_ResumeAudio(int dev)
871 {
872 #if defined(linux)
873     return CDROM_GetStatusCode(ioctl(dev, CDROMRESUME));
874 #elif defined(__FreeBSD__) || defined(__NetBSD__)
875     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESUME, NULL));
876 #else
877     return STATUS_NOT_SUPPORTED;
878 #endif
879 }
880
881 /******************************************************************
882  *              CDROM_StopAudio
883  *
884  *
885  */
886 static DWORD CDROM_StopAudio(int dev)
887 {
888 #if defined(linux)
889     return CDROM_GetStatusCode(ioctl(dev, CDROMSTOP));
890 #elif defined(__FreeBSD__) || defined(__NetBSD__)
891     return CDROM_GetStatusCode(ioctl(dev, CDIOCSTOP, NULL));
892 #else
893     return STATUS_NOT_SUPPORTED;
894 #endif
895 }
896
897 /******************************************************************
898  *              CDROM_GetVolume
899  *
900  *
901  */
902 static DWORD CDROM_GetVolume(int dev, VOLUME_CONTROL* vc)
903 {
904 #if defined(linux)
905     struct cdrom_volctrl volc;
906     int io;
907
908     io = ioctl(dev, CDROMVOLREAD, &volc);
909     if (io != -1)
910     {
911         vc->PortVolume[0] = volc.channel0;
912         vc->PortVolume[1] = volc.channel1;
913         vc->PortVolume[2] = volc.channel2;
914         vc->PortVolume[3] = volc.channel3;
915     }
916     return CDROM_GetStatusCode(io);
917 #elif defined(__FreeBSD__) || defined(__NetBSD__)
918     struct  ioc_vol     volc;
919     int io;
920
921     io = ioctl(dev, CDIOCGETVOL, &volc);
922     if (io != -1)
923     {
924         vc->PortVolume[0] = volc.vol[0];
925         vc->PortVolume[1] = volc.vol[1];
926         vc->PortVolume[2] = volc.vol[2];
927         vc->PortVolume[3] = volc.vol[3];
928     }
929     return CDROM_GetStatusCode(io);
930 #else
931     return STATUS_NOT_SUPPORTED;
932 #endif
933 }
934
935 /******************************************************************
936  *              CDROM_SetVolume
937  *
938  *
939  */
940 static DWORD CDROM_SetVolume(int dev, const VOLUME_CONTROL* vc)
941 {
942 #if defined(linux)
943     struct cdrom_volctrl volc;
944
945     volc.channel0 = vc->PortVolume[0];
946     volc.channel1 = vc->PortVolume[1];
947     volc.channel2 = vc->PortVolume[2];
948     volc.channel3 = vc->PortVolume[3];
949
950     return CDROM_GetStatusCode(ioctl(dev, CDROMVOLCTRL, &volc));
951 #elif defined(__FreeBSD__) || defined(__NetBSD__)
952     struct  ioc_vol     volc;
953
954     volc.vol[0] = vc->PortVolume[0];
955     volc.vol[1] = vc->PortVolume[1];
956     volc.vol[2] = vc->PortVolume[2];
957     volc.vol[3] = vc->PortVolume[3];
958
959     return CDROM_GetStatusCode(ioctl(dev, CDIOCSETVOL, &volc));
960 #else
961     return STATUS_NOT_SUPPORTED;
962 #endif
963 }
964
965 /******************************************************************
966  *              CDROM_RawRead
967  *
968  *
969  */
970 static DWORD CDROM_RawRead(int dev, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
971 {
972     int         ret = STATUS_NOT_SUPPORTED;
973     int         io = -1;
974     DWORD       sectSize;
975
976     switch (raw->TrackMode)
977     {
978     case YellowMode2:   sectSize = 2336;        break;
979     case XAForm2:       sectSize = 2328;        break;
980     case CDDA:          sectSize = 2352;        break;
981     default:    return STATUS_INVALID_PARAMETER;
982     }
983     if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
984     /* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
985      * even if a larger size if read...
986      */
987 #if defined(linux)
988     {
989         struct cdrom_read       cdr;
990         struct cdrom_read_audio cdra;
991
992         switch (raw->TrackMode)
993         {
994         case YellowMode2:
995             if (raw->DiskOffset.s.HighPart) FIXME("Unsupported value\n");
996             cdr.cdread_lba = raw->DiskOffset.s.LowPart; /* FIXME ? */
997             cdr.cdread_bufaddr = buffer;
998             cdr.cdread_buflen = raw->SectorCount * sectSize;
999             io = ioctl(dev, CDROMREADMODE2, &cdr);
1000             break;
1001         case XAForm2:
1002             FIXME("XAForm2: NIY\n");
1003             return ret;
1004         case CDDA:
1005             /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
1006              * between by NT2K box and this... should check on the same drive...
1007              * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
1008              * (linux/NT).
1009              * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
1010              * talking of 0.2 ms of sound
1011              */
1012             /* 2048 = 2 ** 11 */
1013             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1014             cdra.addr.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1015                 (raw->DiskOffset.s.HighPart << (32 - 11))) - 1;
1016             FIXME("reading at %u\n", cdra.addr.lba);
1017             cdra.addr_format = CDROM_LBA;
1018             cdra.nframes = raw->SectorCount;
1019             cdra.buf = buffer;
1020             io = ioctl(dev, CDROMREADAUDIO, &cdra);
1021             break;
1022         }
1023     }
1024 #elif defined(__FreeBSD__)
1025     {
1026         struct ioc_read_audio   ira;
1027
1028         switch (raw->TrackMode)
1029         {
1030         case YellowMode2:
1031             FIXME("YellowMode2: NIY\n");
1032             return ret;
1033         case XAForm2:
1034             FIXME("XAForm2: NIY\n");
1035             return ret;
1036         case CDDA:
1037             /* 2048 = 2 ** 11 */
1038             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1039             ira.address.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1040                 raw->DiskOffset.s.HighPart << (32 - 11)) - 1;
1041             ira.address_format = CD_LBA_FORMAT;
1042             ira.nframes = raw->SectorCount;
1043             ira.buffer = buffer;
1044             io = ioctl(dev, CDIOCREADAUDIO, &ira);
1045             break;
1046         }
1047     }
1048 #elif defined(__NetBSD__)
1049     {
1050         switch (raw->TrackMode)
1051         {
1052         case YellowMode2:
1053             FIXME("YellowMode2: NIY\n");
1054             return ret;
1055         case XAForm2:
1056             FIXME("XAForm2: NIY\n");
1057             return ret;
1058         case CDDA:
1059             FIXME("CDDA: NIY\n");
1060             return ret;
1061         }
1062     }
1063 #endif
1064     *sz = sectSize * raw->SectorCount;
1065     ret = CDROM_GetStatusCode(io);
1066     return ret;
1067 }
1068
1069 /******************************************************************
1070  *              CDROM_ScsiPassThroughDirect
1071  *
1072  *
1073  */
1074 static DWORD CDROM_ScsiPassThroughDirect(int dev, PSCSI_PASS_THROUGH_DIRECT pPacket)
1075 {
1076     int ret = STATUS_NOT_SUPPORTED;
1077 #if defined(linux) && defined(CDROM_SEND_PACKET)
1078     struct linux_cdrom_generic_command cmd;
1079     struct request_sense sense;
1080     int io;
1081
1082     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1083         return STATUS_BUFFER_TOO_SMALL;
1084
1085     if (pPacket->CdbLength > 12)
1086         return STATUS_INVALID_PARAMETER;
1087
1088     if (pPacket->SenseInfoLength > sizeof(sense))
1089         return STATUS_INVALID_PARAMETER;
1090
1091     memset(&cmd, 0, sizeof(cmd));
1092     memset(&sense, 0, sizeof(sense));
1093
1094     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1095
1096     cmd.buffer         = pPacket->DataBuffer;
1097     cmd.buflen         = pPacket->DataTransferLength;
1098     cmd.sense          = &sense;
1099     cmd.quiet          = 0;
1100     cmd.timeout        = pPacket->TimeOutValue*HZ;
1101
1102     switch (pPacket->DataIn)
1103     {
1104     case SCSI_IOCTL_DATA_OUT:
1105         cmd.data_direction = CGC_DATA_WRITE;
1106         break;
1107     case SCSI_IOCTL_DATA_IN:
1108         cmd.data_direction = CGC_DATA_READ;
1109         break;
1110     case SCSI_IOCTL_DATA_UNSPECIFIED:
1111         cmd.data_direction = CGC_DATA_NONE;
1112         break;
1113     default:
1114        return STATUS_INVALID_PARAMETER;
1115     }
1116
1117     io = ioctl(dev, CDROM_SEND_PACKET, &cmd);
1118
1119     if (pPacket->SenseInfoLength != 0)
1120     {
1121         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1122                &sense, pPacket->SenseInfoLength);
1123     }
1124
1125     pPacket->ScsiStatus = cmd.stat;
1126
1127     ret = CDROM_GetStatusCode(io);
1128 #endif
1129     return ret;
1130 }
1131
1132 /******************************************************************
1133  *              CDROM_ScsiPassThrough
1134  *
1135  *
1136  */
1137 static DWORD CDROM_ScsiPassThrough(int dev, PSCSI_PASS_THROUGH pPacket)
1138 {
1139     int ret = STATUS_NOT_SUPPORTED;
1140 #if defined(linux) && defined(CDROM_SEND_PACKET)
1141     struct linux_cdrom_generic_command cmd;
1142     struct request_sense sense;
1143     int io;
1144
1145     if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1146         return STATUS_BUFFER_TOO_SMALL;
1147
1148     if (pPacket->CdbLength > 12)
1149         return STATUS_INVALID_PARAMETER;
1150
1151     if (pPacket->SenseInfoLength > sizeof(sense))
1152         return STATUS_INVALID_PARAMETER;
1153
1154     memset(&cmd, 0, sizeof(cmd));
1155     memset(&sense, 0, sizeof(sense));
1156
1157     memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1158
1159     if ( pPacket->DataBufferOffset > 0x1000 )
1160     {
1161         cmd.buffer     = (void*)pPacket->DataBufferOffset;
1162     }
1163     else
1164     {
1165         cmd.buffer     = ((void*)pPacket) + pPacket->DataBufferOffset;
1166     }
1167     cmd.buflen         = pPacket->DataTransferLength;
1168     cmd.sense          = &sense;
1169     cmd.quiet          = 0;
1170     cmd.timeout        = pPacket->TimeOutValue*HZ;
1171
1172     switch (pPacket->DataIn)
1173     {
1174     case SCSI_IOCTL_DATA_OUT:
1175         cmd.data_direction = CGC_DATA_WRITE;
1176         break;
1177     case SCSI_IOCTL_DATA_IN:
1178         cmd.data_direction = CGC_DATA_READ;
1179         break;
1180     case SCSI_IOCTL_DATA_UNSPECIFIED:
1181         cmd.data_direction = CGC_DATA_NONE;
1182         break;
1183     default:
1184        return STATUS_INVALID_PARAMETER;
1185     }
1186
1187     io = ioctl(dev, CDROM_SEND_PACKET, &cmd);
1188
1189     if (pPacket->SenseInfoLength != 0)
1190     {
1191         memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1192                &sense, pPacket->SenseInfoLength);
1193     }
1194
1195     pPacket->ScsiStatus = cmd.stat;
1196
1197     ret = CDROM_GetStatusCode(io);
1198 #endif
1199     return ret;
1200 }
1201
1202 /******************************************************************
1203  *              CDROM_GetAddress
1204  *
1205  * implements IOCTL_SCSI_GET_ADDRESS
1206  */
1207 static DWORD CDROM_GetAddress(int dev, SCSI_ADDRESS* address)
1208 {
1209     int portnum, targetid;
1210
1211     address->Length = sizeof(SCSI_ADDRESS);
1212     address->PathId = 0; /* bus number */
1213     address->Lun = 0;
1214     if ( ! CDROM_GetIdeInterface(dev, &portnum, &targetid))
1215         return STATUS_NOT_SUPPORTED;
1216
1217     address->PortNumber = portnum;
1218     address->TargetId = targetid;
1219     return 0;
1220 }
1221
1222 /******************************************************************
1223  *              CDROM_DeviceIoControl
1224  *
1225  *
1226  */
1227 BOOL CDROM_DeviceIoControl(DWORD clientID, HANDLE hDevice, DWORD dwIoControlCode,
1228                            LPVOID lpInBuffer, DWORD nInBufferSize,
1229                            LPVOID lpOutBuffer, DWORD nOutBufferSize,
1230                            LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
1231 {
1232     DWORD       sz;
1233     DWORD       error = 0;
1234     int         dev;
1235
1236     TRACE("%lx[%c] %lx %lx %ld %lx %ld %lx %lx\n",
1237           (DWORD)hDevice, 'A' + LOWORD(clientID), dwIoControlCode, (DWORD)lpInBuffer, nInBufferSize,
1238           (DWORD)lpOutBuffer, nOutBufferSize, (DWORD)lpBytesReturned, (DWORD)lpOverlapped);
1239
1240     if (lpBytesReturned) *lpBytesReturned = 0;
1241     if (lpOverlapped)
1242     {
1243         FIXME("Overlapped isn't implemented yet\n");
1244         SetLastError(STATUS_NOT_SUPPORTED);
1245         return FALSE;
1246     }
1247
1248     SetLastError(0);
1249     dev = CDROM_Open(hDevice, clientID);
1250     if (dev == -1)
1251     {
1252         CDROM_GetStatusCode(-1);
1253         return FALSE;
1254     }
1255
1256     switch (dwIoControlCode)
1257     {
1258     case IOCTL_STORAGE_CHECK_VERIFY:
1259     case IOCTL_CDROM_CHECK_VERIFY:
1260         sz = 0;
1261         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1262             error = STATUS_INVALID_PARAMETER;
1263         else error = CDROM_Verify(dev);
1264         break;
1265
1266 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
1267
1268 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
1269 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
1270
1271     case IOCTL_STORAGE_LOAD_MEDIA:
1272     case IOCTL_CDROM_LOAD_MEDIA:
1273         sz = 0;
1274         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1275             error = STATUS_INVALID_PARAMETER;
1276         else error = CDROM_SetTray(dev, FALSE);
1277         break;
1278      case IOCTL_STORAGE_EJECT_MEDIA:
1279         sz = 0;
1280         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1281             error = STATUS_INVALID_PARAMETER;
1282         else error = CDROM_SetTray(dev, TRUE);
1283         break;
1284
1285     case IOCTL_CDROM_MEDIA_REMOVAL:
1286     case IOCTL_DISK_MEDIA_REMOVAL:
1287     case IOCTL_STORAGE_MEDIA_REMOVAL:
1288     case IOCTL_STORAGE_EJECTION_CONTROL:
1289         /* FIXME the last ioctl:s is not the same as the two others...
1290          * lockcount/owner should be handled */
1291         sz = 0;
1292         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1293         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) error = STATUS_BUFFER_TOO_SMALL;
1294         else error = CDROM_ControlEjection(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1295         break;
1296
1297 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
1298
1299     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1300         sz = sizeof(STORAGE_DEVICE_NUMBER);
1301         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1302         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1303         else error = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
1304         break;
1305
1306     case IOCTL_STORAGE_RESET_DEVICE:
1307         sz = 0;
1308         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1309             error = STATUS_INVALID_PARAMETER;
1310         else error = CDROM_ResetAudio(dev);
1311         break;
1312
1313     case IOCTL_CDROM_GET_CONTROL:
1314         sz = sizeof(CDROM_AUDIO_CONTROL);
1315         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1316         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1317         else error = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
1318         break;
1319
1320     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1321         sz = sizeof(DISK_GEOMETRY);
1322         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1323         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1324         else error = CDROM_GetDriveGeometry(dev, (DISK_GEOMETRY*)lpOutBuffer);
1325         break;
1326
1327     case IOCTL_CDROM_DISK_TYPE:
1328         sz = sizeof(CDROM_DISK_DATA);
1329         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1330         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1331         else error = CDROM_GetDiskData(dev, (CDROM_DISK_DATA*)lpOutBuffer);
1332         break;
1333
1334 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
1335
1336     case IOCTL_CDROM_READ_Q_CHANNEL:
1337         sz = sizeof(SUB_Q_CHANNEL_DATA);
1338         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
1339             error = STATUS_INVALID_PARAMETER;
1340         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1341         else error = CDROM_ReadQChannel(dev, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1342                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1343         break;
1344
1345     case IOCTL_CDROM_READ_TOC:
1346         sz = sizeof(CDROM_TOC);
1347         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1348         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1349         else error = CDROM_ReadTOC(dev, (CDROM_TOC*)lpOutBuffer);
1350         break;
1351
1352 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
1353
1354     case IOCTL_CDROM_PAUSE_AUDIO:
1355         sz = 0;
1356         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1357             error = STATUS_INVALID_PARAMETER;
1358         else error = CDROM_PauseAudio(dev);
1359         break;
1360     case IOCTL_CDROM_PLAY_AUDIO_MSF:
1361         sz = 0;
1362         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1363         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1364         else error = CDROM_PlayAudioMSF(dev, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1365         break;
1366     case IOCTL_CDROM_RESUME_AUDIO:
1367         sz = 0;
1368         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1369             error = STATUS_INVALID_PARAMETER;
1370         else error = CDROM_ResumeAudio(dev);
1371         break;
1372     case IOCTL_CDROM_SEEK_AUDIO_MSF:
1373         sz = 0;
1374         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1375         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
1376         else error = CDROM_SeekAudioMSF(dev, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1377         break;
1378     case IOCTL_CDROM_STOP_AUDIO:
1379         sz = 0;
1380         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1381             error = STATUS_INVALID_PARAMETER;
1382         else error = CDROM_StopAudio(dev);
1383         break;
1384     case IOCTL_CDROM_GET_VOLUME:
1385         sz = sizeof(VOLUME_CONTROL);
1386         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1387         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1388         else error = CDROM_GetVolume(dev, (VOLUME_CONTROL*)lpOutBuffer);
1389         break;
1390     case IOCTL_CDROM_SET_VOLUME:
1391         sz = 0;
1392         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1393             error = STATUS_INVALID_PARAMETER;
1394         else error = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
1395         break;
1396     case IOCTL_CDROM_RAW_READ:
1397         sz = 0;
1398         if (nInBufferSize < sizeof(RAW_READ_INFO)) error = STATUS_INVALID_PARAMETER;
1399         else if (lpOutBuffer == NULL) error = STATUS_BUFFER_TOO_SMALL;
1400         else error = CDROM_RawRead(dev, (const RAW_READ_INFO*)lpInBuffer,
1401                                    lpOutBuffer, nOutBufferSize, &sz);
1402         break;
1403     case IOCTL_SCSI_GET_ADDRESS:
1404         sz = sizeof(SCSI_ADDRESS);
1405         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
1406         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
1407         else error = CDROM_GetAddress(dev, (SCSI_ADDRESS*)lpOutBuffer);
1408         break;
1409     case IOCTL_SCSI_PASS_THROUGH_DIRECT:
1410         sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
1411         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1412         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) error = STATUS_BUFFER_TOO_SMALL;
1413         else error = CDROM_ScsiPassThroughDirect(dev, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1414         break;
1415     case IOCTL_SCSI_PASS_THROUGH:
1416         sz = sizeof(SCSI_PASS_THROUGH);
1417         if (lpOutBuffer == NULL) error = STATUS_INVALID_PARAMETER;
1418         else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) error = STATUS_BUFFER_TOO_SMALL;
1419         else error = CDROM_ScsiPassThrough(dev, (PSCSI_PASS_THROUGH)lpOutBuffer);
1420         break;
1421
1422     default:
1423         FIXME("Unsupported IOCTL %lx\n", dwIoControlCode);
1424         sz = 0;
1425         error = STATUS_INVALID_PARAMETER;
1426         break;
1427     }
1428
1429     if (lpBytesReturned) *lpBytesReturned = sz;
1430     if (error)
1431     {
1432         SetLastError(error);
1433         return FALSE;
1434     }
1435     CDROM_Close(clientID, dev);
1436     return TRUE;
1437 }