Storing an IP address in a signed int results in bugs if it starts
[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
9 #include "config.h"
10
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <sys/ioctl.h>
16 #include "ntddk.h"
17 #include "winioctl.h"
18 #include "ntddstor.h"
19 #include "ntddcdrm.h"
20 #include "drive.h"
21 #include "file.h"
22 #include "debugtools.h"
23
24 #ifdef HAVE_LINUX_CDROM_H
25 # include <linux/cdrom.h>
26 #endif
27 #ifdef HAVE_LINUX_UCDROM_H
28 # include <linux/ucdrom.h>
29 #endif
30 #ifdef HAVE_SYS_CDIO_H
31 # include <sys/cdio.h>
32 #endif
33
34 DEFAULT_DEBUG_CHANNEL(cdrom);
35
36 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom 
37  * this should be removed when a proper device interface is implemented
38  */
39 struct cdrom_cache {
40     int fd;
41     int count;
42 };
43 static struct cdrom_cache cdrom_cache[26];
44
45 /******************************************************************
46  *              CDROM_Open
47  *
48  *
49  */
50 static int CDROM_Open(HANDLE hDevice, DWORD clientID)
51 {
52     int dev = LOWORD(clientID);
53
54     if (dev >= 26) return -1;
55
56     if (!cdrom_cache[dev].count)
57     {
58         char root[4];
59
60         strcpy(root, "A:\\");
61         root[0] += dev;
62         if (GetDriveTypeA(root) != DRIVE_CDROM) return -1;
63         cdrom_cache[dev].fd = open(DRIVE_GetDevice(dev), O_RDONLY|O_NONBLOCK);
64         if (cdrom_cache[dev].fd == -1)
65         {
66             FIXME("Can't open %s: %s\n", root, strerror(errno));
67             return -1;
68         }
69     }
70     cdrom_cache[dev].count++;
71     return cdrom_cache[dev].fd;
72 }
73
74 /******************************************************************
75  *              CDROM_Close
76  *
77  *
78  */
79 static void CDROM_Close(DWORD clientID, int fd)
80 {
81     int dev = LOWORD(clientID);
82     
83     if (dev >= 26 || fd != cdrom_cache[dev].fd) FIXME("how come\n");
84     if (--cdrom_cache[dev].count == 0)
85         close(cdrom_cache[dev].fd);
86 }
87
88 /******************************************************************
89  *              CDROM_GetStatusCode
90  *
91  *
92  */
93 static DWORD CDROM_GetStatusCode(int io)
94 {
95     if (io == 0) return 0;
96     switch (errno)
97     {
98     case EIO:   return STATUS_NO_MEDIA_IN_DEVICE;
99     }
100     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
101     return STATUS_IO_DEVICE_ERROR;
102 }
103
104 static DWORD CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
105 {
106     cac->LbaFormat = 0; /* FIXME */
107     cac->LogicalBlocksPerSecond = 1; /* FIXME */
108     return  STATUS_NOT_SUPPORTED;
109 }
110
111 static DWORD CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
112 {
113     return STATUS_NOT_SUPPORTED;
114 }
115
116 static DWORD CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
117 {
118 #if 0
119     dg->Cylinders.s.LowPart = 1; /* FIXME */
120     dg->Cylinders.s.HighPart = 0; /* FIXME */
121     dg->MediaType = 1; /* FIXME */
122     dg->TracksPerCylinder = 1; /* FIXME */
123     dg->SectorsPerTrack = 1; /* FIXME */
124     dg->BytesPerSector= 1; /* FIXME */
125 #endif
126     return STATUS_NOT_SUPPORTED;
127 }
128
129 /**************************************************************************
130  *                              CDROM_Reset                     [internal]
131  */
132 static DWORD CDROM_ResetAudio(int dev)
133 {
134 #if defined(linux)
135     return CDROM_GetStatusCode(ioctl(dev, CDROMRESET));
136 #elif defined(__FreeBSD__) || defined(__NetBSD__)
137     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESET, NULL));
138 #else
139     return STATUS_NOT_SUPPORTED;
140 #endif
141 }  
142
143 /******************************************************************
144  *              CDROM_SetTray
145  *
146  *
147  */
148 static DWORD CDROM_SetTray(int dev, BOOL doEject)
149 {
150 #if defined(linux)
151     return CDROM_GetStatusCode(ioctl(dev, doEject ? CDROMEJECT : CDROMCLOSETRAY));
152 #elif defined(__FreeBSD__) || defined(__NetBSD__)
153     return CDROM_GetStatusCode((ioctl(dev, CDIOCALLOW, NULL)) || 
154                                (ioctl(dev, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
155                                (ioctl(dev, CDIOCPREVENT, NULL)));
156 #else
157     return STATUS_NOT_SUPPORTED;
158 #endif
159 }
160
161 /******************************************************************
162  *              CDROM_ControlEjection
163  *
164  *
165  */
166 static DWORD CDROM_ControlEjection(int dev, const PREVENT_MEDIA_REMOVAL* rmv)
167 {
168     int val = rmv->PreventMediaRemoval;
169 #if defined(linux)
170     return CDROM_GetStatusCode(ioctl(dev, CDROM_LOCKDOOR, val));
171 #elif defined(__FreeBSD__) || defined(__NetBSD__)
172     return CDROM_GetStatusCode(ioctl(dev, (val) ? CDIOCPREVENT : CDIOCALLOW, NULL));
173 #else
174     return STATUS_NOT_SUPPORTED;
175 #endif
176 }
177
178 /******************************************************************
179  *              CDROM_ReadTOC
180  *
181  *
182  */
183 static DWORD CDROM_ReadTOC(int dev, CDROM_TOC* toc)
184 {
185     DWORD       ret = STATUS_NOT_SUPPORTED;
186
187 #if defined(linux)
188     int                         i, io = -1;
189     struct cdrom_tochdr         hdr;
190     struct cdrom_tocentry       entry;
191
192     io = ioctl(dev, CDROMREADTOCHDR, &hdr);
193     if (io == -1) 
194     {
195         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
196         goto end;
197     }
198     toc->FirstTrack = hdr.cdth_trk0;
199     toc->LastTrack  = hdr.cdth_trk1;
200
201     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
202
203     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
204     {
205         if (i == toc->LastTrack + 1)
206         {
207             entry.cdte_track = CDROM_LEADOUT;
208         } else {
209             entry.cdte_track = i;
210         }
211         entry.cdte_format = CDROM_MSF;
212         io = ioctl(dev, CDROMREADTOCENTRY, &entry);
213         if (io == -1) {
214             WARN("error read entry (%s)\n", strerror(errno));
215             goto end;
216         }
217         toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
218         toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
219         /* marking last track with leadout value as index */
220         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
221         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
222         toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
223         toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
224         toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
225     }
226 end:
227     ret = CDROM_GetStatusCode(io);
228 #elif defined(__FreeBSD__) || defined(__NetBSD__)
229     int                         i, io = -1;
230     struct ioc_toc_header       hdr;
231     struct ioc_read_toc_entry   entry;
232     struct cd_toc_entry         toc_buffer;
233
234     io = ioctl(dev, CDIOREADTOCHEADER, &hdr);
235     if (io == -1) 
236     {
237         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
238         goto end;
239     }
240     toc->FirstTrack = hdr.starting_track;
241     toc->LastTrack  = hdr.ending_track;
242
243     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
244
245     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
246     {
247         if (i == toc->LastTrack + 1)
248         {
249 #define LEADOUT 0xaa
250             entry.starting_track = LEADOUT;
251         } else {
252             entry.starting_track = i;
253         }
254         memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
255         entry.address_format = CD_MSF_FORMAT;
256         entry.data_len = sizeof(toc_buffer);
257         entry.data = &toc_buffer;
258         io = ioctl(dev, CDIOREADTOCENTRYS, &entry);
259         if (io == -1) {
260             WARN("error read entry (%s)\n", strerror(errno));
261             goto end;
262         }
263         toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
264         toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
265         /* marking last track with leadout value as index */
266         toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
267         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
268         toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
269         toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
270         toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
271     }
272 end:
273     ret = CDROM_GetStatusCode(io);
274 #else
275     ret = STATUS_NOT_SUPPORTED;
276 #endif
277     return ret;
278 }
279
280 /******************************************************************
281  *              CDROM_GetDiskData
282  *
283  *
284  */
285 static DWORD CDROM_GetDiskData(int dev, CDROM_DISK_DATA* data)
286 {
287     CDROM_TOC   toc;
288     DWORD       ret;
289     int         i;
290
291     if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
292     data->DiskData = 0;
293     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
294         if (toc.TrackData[i].Control & 0x04)
295             data->DiskData |= CDROM_DISK_DATA_TRACK;
296         else
297             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
298     }
299     return 0;
300 }
301
302 /******************************************************************
303  *              CDROM_ReadQChannel
304  *
305  *
306  */
307 static DWORD CDROM_ReadQChannel(int dev, const CDROM_SUB_Q_DATA_FORMAT* fmt, 
308                                 SUB_Q_CHANNEL_DATA* data)
309 {
310     DWORD               ret = STATUS_NOT_SUPPORTED;
311     unsigned            size;
312     SUB_Q_HEADER*       hdr = (SUB_Q_HEADER*)data;
313     int                 io;
314
315 #ifdef linux
316     struct cdrom_subchnl        sc;
317     sc.cdsc_format = CDROM_MSF;
318
319     io = ioctl(dev, CDROMSUBCHNL, &sc);
320     if (io == -1)
321     {
322         TRACE("opened or no_media (%s)!\n", strerror(errno));
323         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
324         goto end;
325     }
326
327     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
328
329     switch (sc.cdsc_audiostatus) {
330     case CDROM_AUDIO_INVALID:
331         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
332         break;
333     case CDROM_AUDIO_NO_STATUS: 
334         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
335         break;
336     case CDROM_AUDIO_PLAY:
337         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
338         break;
339     case CDROM_AUDIO_PAUSED:
340         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
341         break;
342     case CDROM_AUDIO_COMPLETED:
343         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
344         break;
345     case CDROM_AUDIO_ERROR:
346         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
347         break;
348     default:
349         TRACE("status=%02X !\n", sc.cdsc_audiostatus);
350         break;
351     }
352     switch (fmt->Format)
353     {
354     case IOCTL_CDROM_CURRENT_POSITION:
355         size = sizeof(SUB_Q_CURRENT_POSITION);
356         data->CurrentPosition.FormatCode = sc.cdsc_format;
357         data->CurrentPosition.Control = sc.cdsc_ctrl;
358         data->CurrentPosition.ADR = sc.cdsc_adr;
359         data->CurrentPosition.TrackNumber = sc.cdsc_trk;
360         data->CurrentPosition.IndexNumber = sc.cdsc_ind;
361
362         data->CurrentPosition.AbsoluteAddress[0] = 0;
363         data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
364         data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
365         data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
366
367         data->CurrentPosition.TrackRelativeAddress[0] = 0;
368         data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
369         data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
370         data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
371         break;
372     case IOCTL_CDROM_MEDIA_CATALOG:
373         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
374         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
375         {
376             struct cdrom_mcn mcn;
377             if ((io = ioctl(dev, CDROM_GET_MCN, &mcn)) == -1) goto end;
378
379             data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
380             data->MediaCatalog.Mcval = 0; /* FIXME */
381             memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
382             data->MediaCatalog.MediaCatalog[14] = 0;
383         }
384         break;
385     case IOCTL_CDROM_TRACK_ISRC:
386         size = sizeof(SUB_Q_CURRENT_POSITION);
387         FIXME("TrackIsrc: NIY on linux");
388         data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
389         data->TrackIsrc.Tcval = 0;
390         io = 0;
391         break;
392     }
393
394  end:
395     ret = CDROM_GetStatusCode(io);
396 #elif defined(__FreeBSD__) || defined(__NetBSD__)
397     struct ioc_read_subchannel  read_sc;
398     struct cd_sub_channel_info  sc;
399
400     read_sc.address_format = CD_MSF_FORMAT;
401     read_sc.track          = 0;
402     read_sc.data_len       = sizeof(sc);
403     read_sc.data           = &sc;
404     switch (fmt->Format)
405     {
406     case IOCTL_CDROM_CURRENT_POSITION:
407         read_sc.data_format    = CD_CURRENT_POSITION;
408         break;
409     case IOCTL_CDROM_MEDIA_CATALOG:
410         read_sc.data_format    = CD_MEDIA_CATALOG;
411         break;
412     case IOCTL_CDROM_TRACK_ISRC:
413         read_sc.data_format    = CD_TRACK_INFO;
414         sc.what.track_info.track_number = data->TrackIsrc.Track;
415         break;
416     }
417     io = ioctl(dev, CDIOCREADSUBCHANNEL, &read_sc);
418     if (io == -1)
419     {
420         TRACE("opened or no_media (%s)!\n", strerror(errno));
421         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
422         goto end;
423     }
424
425     hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
426
427     switch (sc.header.audio_status) {
428     case CD_AS_AUDIO_INVALID:
429         hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
430         break;
431     case CD_AS_NO_STATUS:
432         hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
433         break;
434     case CD_AS_PLAY_IN_PROGRESS:
435         hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
436         break;
437     case CD_AS_PLAY_PAUSED:
438         hdr->AudioStatus = AUDIO_STATUS_PAUSED;
439         break;
440     case CD_AS_PLAY_COMPLETED:
441         hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
442         break;
443     case CD_AS_PLAY_ERROR:
444         hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
445         break;
446     default:
447         TRACE("status=%02X !\n", sc.header.audio_status);
448     }
449     switch (fmt->Format)
450     {
451     case IOCTL_CDROM_CURRENT_POSITION:
452         size = sizeof(SUB_Q_CURRENT_POSITION);
453         data->CurrentPosition.FormatCode = sc.what.position.data_format;
454         data->CurrentPosition.Control = sc.what.position.control;
455         data->CurrentPosition.ADR = sc.what.position.addr_type;
456         data->CurrentPosition.TrackNumber = sc.what.position.track_number;
457         data->CurrentPosition.IndexNumber = sc.what.position.index_number;
458
459         data->CurrentPosition.AbsoluteAddress[0] = 0;
460         data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
461         data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
462         data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
463         data->CurrentPosition.TrackRelativeAddress[0] = 0;
464         data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
465         data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
466         data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
467         break;
468     case IOCTL_CDROM_MEDIA_CATALOG:
469         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
470         data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
471         data->MediaCatalog.FormatCode = sc.what.media_catalog.data_format;
472         data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
473         memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
474         break;
475     case IOCTL_CDROM_TRACK_ISRC:
476         size = sizeof(SUB_Q_CURRENT_POSITION);
477         data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
478         memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
479         break;
480     }
481
482  end:
483     ret = CDROM_GetStatusCode(io);
484 #endif
485     return ret;
486 }
487
488 /******************************************************************
489  *              CDROM_Verify
490  *
491  *
492  */
493 static DWORD CDROM_Verify(int dev)
494 {
495     /* quick implementation */
496     CDROM_SUB_Q_DATA_FORMAT     fmt;
497     SUB_Q_CHANNEL_DATA          data;
498
499     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
500     return CDROM_ReadQChannel(dev, &fmt, &data) ? 0 : 1;
501 }
502
503 /******************************************************************
504  *              CDROM_PlayAudioMSF
505  *
506  *
507  */
508 static DWORD CDROM_PlayAudioMSF(int dev, const CDROM_PLAY_AUDIO_MSF* audio_msf)
509 {
510     DWORD       ret = STATUS_NOT_SUPPORTED;
511 #ifdef linux
512     struct      cdrom_msf       msf;
513     int         io;
514
515     msf.cdmsf_min0   = audio_msf->StartingM;
516     msf.cdmsf_sec0   = audio_msf->StartingS;
517     msf.cdmsf_frame0 = audio_msf->StartingF;
518     msf.cdmsf_min1   = audio_msf->EndingM;
519     msf.cdmsf_sec1   = audio_msf->EndingS;
520     msf.cdmsf_frame1 = audio_msf->EndingF;
521
522     io = ioctl(dev, CDROMSTART);
523     if (io == -1)
524     {
525         WARN("motor doesn't start !\n");
526         goto end;
527     }
528     io = ioctl(dev, CDROMPLAYMSF, &msf);
529     if (io == -1)
530     {
531         WARN("device doesn't play !\n");
532         goto end;
533     }
534     TRACE("msf = %d:%d:%d %d:%d:%d\n",
535           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
536           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
537  end:
538     ret = CDROM_GetStatusCode(io);
539 #elif defined(__FreeBSD__) || defined(__NetBSD__)
540     struct      ioc_play_msf    msf;
541     int         io;
542
543     msf.start_m      = audio_msf->StartingM;
544     msf.start_s      = audio_msf->StartingS;
545     msf.start_f      = audio_msf->StartingF;
546     msf.end_m        = audio_msf->EndingM;
547     msf.end_s        = audio_msf->EndingS;
548     msf.end_f        = audio_msf->EndingF;
549
550     io = ioctl(dev, CDIOCSTART, NULL);
551     if (io == -1)
552     {
553         WARN("motor doesn't start !\n");
554         goto end;
555     }
556     io = ioctl(dev, CDIOCPLAYMSF, &msf);
557     if (io == -1)
558     {
559         WARN("device doesn't play !\n");
560         goto end;
561     }
562     TRACE("msf = %d:%d:%d %d:%d:%d\n",
563           msf.start_m, msf.start_s, msf.start_f,
564           msf.end_m,   msf.end_s,   msf.end_f);
565 end:
566     ret = CDROM_GetStatusCode(io);
567 #endif
568     return ret;
569 }
570
571 /******************************************************************
572  *              CDROM_SeekAudioMSF
573  *
574  *
575  */
576 static DWORD CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
577 {
578 #if defined(linux)
579     struct cdrom_msf0   msf;
580     msf.minute = audio_msf->M;
581     msf.second = audio_msf->S;
582     msf.frame  = audio_msf->F;
583
584     return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
585 #elif defined(__FreeBSD__) || defined(__NetBSD__)
586     FIXME("Could a BSD expert implement the seek function ?\n");
587     return STATUS_NOT_SUPPORTED;
588 #else
589     return STATUS_NOT_SUPPORTED;
590 #endif
591 }
592
593 /******************************************************************
594  *              CDROM_PauseAudio
595  *
596  *
597  */
598 static DWORD CDROM_PauseAudio(int dev)
599 {
600 #if defined(linux)
601     return CDROM_GetStatusCode(ioctl(dev, CDROMPAUSE));
602 #elif defined(__FreeBSD__) || defined(__NetBSD__)
603     return CDROM_GetStatusCode(ioctl(dev, CDIOCPAUSE, NULL));
604 #else
605     return STATUS_NOT_SUPPORTED;
606 #endif
607 }
608
609 /******************************************************************
610  *              CDROM_ResumeAudio
611  *
612  *
613  */
614 static DWORD CDROM_ResumeAudio(int dev)
615 {
616 #if defined(linux)
617     return CDROM_GetStatusCode(ioctl(dev, CDROMRESUME));
618 #elif defined(__FreeBSD__) || defined(__NetBSD__)
619     return CDROM_GetStatusCode(ioctl(dev, CDIOCRESUME, NULL));
620 #else
621     return STATUS_NOT_SUPPORTED;
622 #endif
623 }
624
625 /******************************************************************
626  *              CDROM_StopAudio
627  *
628  *
629  */
630 static DWORD CDROM_StopAudio(int dev)
631 {
632 #if defined(linux)
633     return CDROM_GetStatusCode(ioctl(dev, CDROMSTOP));
634 #elif defined(__FreeBSD__) || defined(__NetBSD__)
635     return CDROM_GetStatusCode(ioctl(dev, CDIOCSTOP, NULL));
636 #else
637     return STATUS_NOT_SUPPORTED;
638 #endif
639 }
640
641 /******************************************************************
642  *              CDROM_GetVolume
643  *
644  *
645  */
646 static DWORD CDROM_GetVolume(int dev, VOLUME_CONTROL* vc)
647 {
648 #if defined(linux)
649     struct cdrom_volctrl volc;
650     int io;
651     
652     io = ioctl(dev, CDROMVOLREAD, &volc);
653     if (io != -1)
654     {
655         vc->PortVolume[0] = volc.channel0;
656         vc->PortVolume[1] = volc.channel1;
657         vc->PortVolume[2] = volc.channel2;
658         vc->PortVolume[3] = volc.channel3;
659     }
660     return CDROM_GetStatusCode(io);
661 #elif defined(__FreeBSD__) || defined(__NetBSD__)
662     struct  ioc_vol     volc;
663     int io;
664     
665     io = ioctl(dev, CDIOCGETVOL, &volc);
666     if (io != -1)
667     {
668         vc->PortVolume[0] = volc.vol[0];
669         vc->PortVolume[1] = volc.vol[1];
670         vc->PortVolume[2] = volc.vol[2];
671         vc->PortVolume[3] = volc.vol[3];
672     } 
673     return CDROM_GetStatusCode(io);
674 #else
675     return STATUS_NOT_SUPPORTED;
676 #endif
677 }
678
679 /******************************************************************
680  *              CDROM_SetVolume
681  *
682  *
683  */
684 static DWORD CDROM_SetVolume(int dev, const VOLUME_CONTROL* vc)
685 {
686 #if defined(linux)
687     struct cdrom_volctrl volc;
688
689     volc.channel0 = vc->PortVolume[0];
690     volc.channel1 = vc->PortVolume[1];
691     volc.channel2 = vc->PortVolume[2];
692     volc.channel3 = vc->PortVolume[3];
693    
694     return CDROM_GetStatusCode(ioctl(dev, CDROMVOLCTRL, &volc));
695 #elif defined(__FreeBSD__) || defined(__NetBSD__)
696     struct  ioc_vol     volc;
697
698     volc.vol[0] = vc->PortVolume[0];
699     volc.vol[1] = vc->PortVolume[1];
700     volc.vol[2] = vc->PortVolume[2];
701     volc.vol[3] = vc->PortVolume[3];
702
703     return CDROM_GetStatusCode(ioctl(dev, CDIOCSETVOL, &volc));
704 #else
705     return STATUS_NOT_SUPPORTED;
706 #endif
707 }
708
709 /******************************************************************
710  *              CDROM_RawRead
711  *
712  *
713  */
714 static DWORD CDROM_RawRead(int dev, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
715 {
716     int         ret = STATUS_NOT_SUPPORTED;
717     int         io = -1;
718     DWORD       sectSize;
719
720     switch (raw->TrackMode)
721     {
722     case YellowMode2:   sectSize = 2336;        break;
723     case XAForm2:       sectSize = 2328;        break;
724     case CDDA:          sectSize = 2352;        break;
725     default:    return STATUS_INVALID_PARAMETER;
726     }
727     if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
728     /* strangely enough, it seems that sector offsets are always indicated with a size of 2048, 
729      * even if a larger size if read...
730      */
731 #if defined(linux)
732     {
733         struct cdrom_read       cdr;
734         struct cdrom_read_audio cdra;
735
736         switch (raw->TrackMode)
737         {
738         case YellowMode2:
739             if (raw->DiskOffset.s.HighPart) FIXME("Unsupported value\n");
740             cdr.cdread_lba = raw->DiskOffset.s.LowPart; /* FIXME ? */
741             cdr.cdread_bufaddr = buffer;
742             cdr.cdread_buflen = raw->SectorCount * sectSize;
743             io = ioctl(dev, CDROMREADMODE2, &cdr);
744             break;
745         case XAForm2:
746             FIXME("XAForm2: NIY\n");
747             return ret;
748         case CDDA:
749             /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
750              * between by NT2K box and this... should check on the same drive... 
751              * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
752              * (linux/NT).
753              * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
754              * talking of 0.2 ms of sound
755              */
756             /* 2048 = 2 ** 11 */
757             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
758             cdra.addr.lba = ((raw->DiskOffset.s.LowPart >> 11) |
759                 (raw->DiskOffset.s.HighPart << (32 - 11))) - 1;
760             FIXME("reading at %u\n", cdra.addr.lba);
761             cdra.addr_format = CDROM_LBA;
762             cdra.nframes = raw->SectorCount;
763             cdra.buf = buffer;
764             io = ioctl(dev, CDROMREADAUDIO, &cdra);
765             break;
766         }
767     }
768 #elif defined(__FreeBSD__)
769     {
770         struct ioc_read_audio   ira;
771
772         switch (raw->TrackMode)
773         {
774         case YellowMode2:
775             FIXME("YellowMode2: NIY\n");
776             return ret;
777         case XAForm2:
778             FIXME("XAForm2: NIY\n");
779             return ret;
780         case CDDA:
781             /* 2048 = 2 ** 11 */
782             if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
783             ira.address.lba = ((raw->DiskOffset.s.LowPart >> 11) |
784                 raw->DiskOffset.s.HighPart << (32 - 11)) - 1;
785             ira.address_format = CD_LBA_FORMAT;
786             ira.nframes = raw->SectorCount;
787             ira.buffer = buffer;
788             io = ioctl(dev, CDIOCREADAUDIO, &ira);
789             break;
790         }
791     }
792 #elif defined(__NetBSD__)
793     {
794         switch (raw->TrackMode)
795         {
796         case YellowMode2:
797             FIXME("YellowMode2: NIY\n");
798             return ret;
799         case XAForm2:
800             FIXME("XAForm2: NIY\n");
801             return ret;
802         case CDDA:
803             FIXME("CDDA: NIY\n");
804             return ret;
805         }
806     }
807 #endif
808     *sz = sectSize * raw->SectorCount;
809     ret = CDROM_GetStatusCode(io);
810     return ret;
811 }
812
813 /******************************************************************
814  *              CDROM_DeviceIoControl
815  *
816  *
817  */
818 BOOL CDROM_DeviceIoControl(DWORD clientID, HANDLE hDevice, DWORD dwIoControlCode, 
819                            LPVOID lpInBuffer, DWORD nInBufferSize, 
820                            LPVOID lpOutBuffer, DWORD nOutBufferSize,
821                            LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
822 {
823     DWORD       sz;
824     DWORD       error = 0;
825     int         dev;
826
827     TRACE("%lx[%c] %lx %lx %ld %lx %ld %lx %lx\n", 
828           (DWORD)hDevice, 'A' + LOWORD(clientID), dwIoControlCode, (DWORD)lpInBuffer, nInBufferSize, 
829           (DWORD)lpOutBuffer, nOutBufferSize, (DWORD)lpBytesReturned, (DWORD)lpOverlapped);
830
831     if (lpBytesReturned) *lpBytesReturned = 0;
832     if (lpOverlapped)
833     {
834         FIXME("Overlapped isn't implemented yet\n");
835         SetLastError(STATUS_NOT_SUPPORTED);
836         return FALSE;
837     }
838
839     SetLastError(0);
840     dev = CDROM_Open(hDevice, clientID);
841     if (dev == -1)
842     {
843         CDROM_GetStatusCode(-1);
844         return FALSE;
845     }
846
847     switch (dwIoControlCode)
848     {
849     case IOCTL_STORAGE_CHECK_VERIFY:
850     case IOCTL_CDROM_CHECK_VERIFY:
851         sz = 0;
852         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
853             error = STATUS_INVALID_PARAMETER;
854         else error = CDROM_Verify(dev);
855         break;
856
857 /* EPP     case IOCTL_STORAGE_CHECK_VERIFY2: */
858
859 /* EPP     case IOCTL_STORAGE_FIND_NEW_DEVICES: */
860 /* EPP     case IOCTL_CDROM_FIND_NEW_DEVICES: */
861
862     case IOCTL_STORAGE_LOAD_MEDIA:
863     case IOCTL_CDROM_LOAD_MEDIA:
864         sz = 0;
865         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
866             error = STATUS_INVALID_PARAMETER;
867         else error = CDROM_SetTray(dev, FALSE);
868         break;
869      case IOCTL_STORAGE_EJECT_MEDIA:
870         sz = 0;
871         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
872             error = STATUS_INVALID_PARAMETER;
873         else error = CDROM_SetTray(dev, TRUE);
874         break;
875
876     case IOCTL_DISK_MEDIA_REMOVAL:
877     case IOCTL_STORAGE_MEDIA_REMOVAL:
878     case IOCTL_STORAGE_EJECTION_CONTROL:
879         /* FIXME the last ioctl:s is not the same as the two others... 
880          * lockcount/owner should be handled */
881         sz = 0;
882         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
883         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) error = STATUS_BUFFER_TOO_SMALL;
884         else error = CDROM_ControlEjection(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
885         break;
886
887 /* EPP     case IOCTL_STORAGE_GET_MEDIA_TYPES: */
888
889     case IOCTL_STORAGE_GET_DEVICE_NUMBER:
890         sz = sizeof(STORAGE_DEVICE_NUMBER);
891         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
892         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
893         else error = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
894         break;
895
896     case IOCTL_STORAGE_RESET_DEVICE:
897         sz = 0;
898         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
899             error = STATUS_INVALID_PARAMETER;
900         else error = CDROM_ResetAudio(dev);
901         break;
902
903     case IOCTL_CDROM_GET_CONTROL:
904         sz = sizeof(CDROM_AUDIO_CONTROL);
905         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
906         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
907         else error = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
908         break;
909
910     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
911         sz = sizeof(DISK_GEOMETRY);
912         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
913         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
914         else error = CDROM_GetDriveGeometry(dev, (DISK_GEOMETRY*)lpOutBuffer);
915         break;
916
917     case IOCTL_CDROM_DISK_TYPE:
918         sz = sizeof(CDROM_DISK_DATA);
919         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
920         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
921         else error = CDROM_GetDiskData(dev, (CDROM_DISK_DATA*)lpOutBuffer);
922         break;
923
924 /* EPP     case IOCTL_CDROM_GET_LAST_SESSION: */
925
926     case IOCTL_CDROM_READ_Q_CHANNEL:
927         sz = sizeof(SUB_Q_CHANNEL_DATA);
928         if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT)) 
929             error = STATUS_INVALID_PARAMETER;
930         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
931         else error = CDROM_ReadQChannel(dev, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
932                                         (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
933         break;
934
935     case IOCTL_CDROM_READ_TOC:
936         sz = sizeof(CDROM_TOC);
937         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
938         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
939         else error = CDROM_ReadTOC(dev, (CDROM_TOC*)lpOutBuffer);
940         break;
941
942 /* EPP     case IOCTL_CDROM_READ_TOC_EX: */
943
944     case IOCTL_CDROM_PAUSE_AUDIO:
945         sz = 0;
946         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
947             error = STATUS_INVALID_PARAMETER;
948         else error = CDROM_PauseAudio(dev);
949         break;
950     case IOCTL_CDROM_PLAY_AUDIO_MSF:
951         sz = 0;
952         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
953         else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
954         else error = CDROM_PlayAudioMSF(dev, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
955         break;
956     case IOCTL_CDROM_RESUME_AUDIO:
957         sz = 0;
958         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
959             error = STATUS_INVALID_PARAMETER;
960         else error = CDROM_ResumeAudio(dev);
961         break;
962     case IOCTL_CDROM_SEEK_AUDIO_MSF:
963         sz = 0;
964         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
965         else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) error = STATUS_BUFFER_TOO_SMALL;
966         else error = CDROM_SeekAudioMSF(dev, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
967         break;
968     case IOCTL_CDROM_STOP_AUDIO:
969         sz = 0;
970         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
971             error = STATUS_INVALID_PARAMETER;
972         else error = CDROM_StopAudio(dev);
973         break;
974     case IOCTL_CDROM_GET_VOLUME:
975         sz = sizeof(VOLUME_CONTROL);
976         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
977         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
978         else error = CDROM_GetVolume(dev, (VOLUME_CONTROL*)lpOutBuffer);
979         break;
980     case IOCTL_CDROM_SET_VOLUME:
981         sz = 0;
982         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
983             error = STATUS_INVALID_PARAMETER;
984         else error = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
985         break;
986     case IOCTL_CDROM_RAW_READ:
987         sz = 0;
988         if (nInBufferSize < sizeof(RAW_READ_INFO)) error = STATUS_INVALID_PARAMETER;
989         else if (lpOutBuffer == NULL) error = STATUS_BUFFER_TOO_SMALL;
990         else error = CDROM_RawRead(dev, (const RAW_READ_INFO*)lpInBuffer, 
991                                    lpOutBuffer, nOutBufferSize, &sz);
992         break;
993     default:
994         FIXME("Unsupported IOCTL %lx\n", dwIoControlCode);
995         sz = 0;
996         error = STATUS_INVALID_PARAMETER;
997         break;
998     }
999
1000     if (lpBytesReturned) *lpBytesReturned = sz;
1001     if (error) 
1002     {
1003         SetLastError(error);
1004         return FALSE;
1005     }
1006     CDROM_Close(clientID, dev);
1007     return TRUE;
1008 }
1009