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