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