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