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