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