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