Improved (sort of) RegFlushKey.
[wine] / misc / cdrom.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Sample CDAUDIO Wine Driver for Linux
4  *
5  * Copyright 1994 Martin Ayotte
6  * Copyright 1999 Eric Pouech
7  */
8
9 #include <errno.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #include "cdrom.h"
14 #include "debugtools.h"
15
16 DEFAULT_DEBUG_CHANNEL(cdaudio)
17
18 #if defined(__NetBSD__)
19 # define CDAUDIO_DEV "/dev/rcd0d"
20 #elif defined(__FreeBSD__)
21 # define CDAUDIO_DEV "/dev/rcd0c"
22 #else
23 # define CDAUDIO_DEV "/dev/cdrom"
24 #endif
25
26 #define MAX_CDAUDIO_TRACKS      256
27
28 /**************************************************************************
29  *                              CDAUDIO_Open                    [internal]
30  */
31 int     CDAUDIO_Open(WINE_CDAUDIO* wcda)
32 {
33 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
34     wcda->unixdev = open(CDAUDIO_DEV, O_RDONLY | O_NONBLOCK, 0);
35     if (wcda->unixdev == -1) {
36         WARN("can't open '%s'!.  errno=%d\n", CDAUDIO_DEV, errno);
37         return -1;
38     }
39     wcda->cdaMode = WINE_CDA_OPEN;      /* to force reading tracks info */
40     wcda->nCurTrack = 0;
41     wcda->nTracks = 0;
42     wcda->dwTotalLen = 0;
43     wcda->dwFirstOffset = 0;
44     wcda->lpdwTrackLen = NULL;
45     wcda->lpdwTrackPos = NULL;
46     wcda->lpbTrackFlags = NULL;
47     return 0;
48 #else
49     wcda->unixdev = -1;
50     return -1;
51 #endif
52 }
53
54 /**************************************************************************
55  *                              CDAUDIO_Close                   [internal]
56  */
57 int     CDAUDIO_Close(WINE_CDAUDIO* wcda)
58 {
59 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
60     if (wcda->lpdwTrackLen != NULL) free(wcda->lpdwTrackLen);
61     if (wcda->lpdwTrackPos != NULL) free(wcda->lpdwTrackPos);
62     if (wcda->lpbTrackFlags != NULL) free(wcda->lpbTrackFlags);
63     close(wcda->unixdev);
64     return 0;
65 #else
66     return -1;
67 #endif
68 }
69
70 /**************************************************************************
71  *                              CDAUDIO_GetNumberOfTracks       [internal]
72  */
73 UINT16 CDAUDIO_GetNumberOfTracks(WINE_CDAUDIO* wcda)
74 {
75 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
76 #ifdef linux
77     struct cdrom_tochdr         hdr;
78 #else
79     struct ioc_toc_header       hdr;
80 #endif
81     
82     if (wcda->nTracks == 0) {
83 #ifdef linux
84         if (ioctl(wcda->unixdev, CDROMREADTOCHDR, &hdr))
85 #else
86         if (ioctl(wcda->unixdev, CDIOREADTOCHEADER, &hdr))
87 #endif
88         {
89             WARN("(%p) -- Error occured (%d)!\n", wcda, errno);
90             return (WORD)-1;
91         }
92 #ifdef linux
93         wcda->nFirstTrack = hdr.cdth_trk0;
94         wcda->nLastTrack  = hdr.cdth_trk1;
95 #else   
96         wcda->nFirstTrack = hdr.starting_track;
97         wcda->nLastTrack  = hdr.ending_track;
98 #endif
99         wcda->nTracks = wcda->nLastTrack - wcda->nFirstTrack + 1;
100     }
101     return wcda->nTracks;
102 #else
103     return (WORD)-1;
104 #endif
105 }
106
107 /**************************************************************************
108  *                      CDAUDIO_GetTracksInfo                   [internal]
109  */
110 BOOL CDAUDIO_GetTracksInfo(WINE_CDAUDIO* wcda)
111 {
112 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
113     int         i, length;
114     int         start, last_start = 0;
115     int         total_length = 0;
116 #ifdef linux
117     struct cdrom_tocentry       entry;
118 #else
119     struct ioc_read_toc_entry   entry;
120     struct cd_toc_entry         toc_buffer;
121 #endif
122     
123     if (wcda->nTracks == 0) {
124         if (CDAUDIO_GetNumberOfTracks(wcda) == (WORD)-1) return FALSE;
125     }
126     TRACE("nTracks=%u\n", wcda->nTracks);
127     
128     if (wcda->lpdwTrackLen != NULL) 
129         free(wcda->lpdwTrackLen);
130     wcda->lpdwTrackLen = (LPDWORD)malloc((wcda->nTracks + 1) * sizeof(DWORD));
131     if (wcda->lpdwTrackPos != NULL) 
132         free(wcda->lpdwTrackPos);
133     wcda->lpdwTrackPos = (LPDWORD)malloc((wcda->nTracks + 1) * sizeof(DWORD));
134     if (wcda->lpbTrackFlags != NULL)
135         free(wcda->lpbTrackFlags);
136     wcda->lpbTrackFlags = (LPBYTE)malloc((wcda->nTracks + 1) * sizeof(BYTE));
137     if (wcda->lpdwTrackLen == NULL || wcda->lpdwTrackPos == NULL ||
138         wcda->lpbTrackFlags == NULL) {
139         WARN("error allocating track table !\n");
140         return FALSE;
141     }
142     memset(wcda->lpdwTrackLen, 0, (wcda->nTracks + 1) * sizeof(DWORD));
143     memset(wcda->lpdwTrackPos, 0, (wcda->nTracks + 1) * sizeof(DWORD));
144     memset(wcda->lpbTrackFlags, 0, (wcda->nTracks + 1) * sizeof(BYTE));
145     for (i = 0; i <= wcda->nTracks; i++) {
146         if (i == wcda->nTracks)
147 #ifdef linux
148             entry.cdte_track = CDROM_LEADOUT;
149 #else
150 #define LEADOUT 0xaa
151         entry.starting_track = LEADOUT; /* XXX */
152 #endif
153         else
154 #ifdef linux
155             entry.cdte_track = i + 1;
156 #else
157         entry.starting_track = i + 1;
158 #endif
159 #ifdef linux
160         entry.cdte_format = CDROM_MSF;
161 #else
162         bzero((char *)&toc_buffer, sizeof(toc_buffer));
163         entry.address_format = CD_MSF_FORMAT;
164         entry.data_len = sizeof(toc_buffer);
165         entry.data = &toc_buffer;
166 #endif
167 #ifdef linux
168         if (ioctl(wcda->unixdev, CDROMREADTOCENTRY, &entry))
169 #else
170         if (ioctl(wcda->unixdev, CDIOREADTOCENTRYS, &entry))
171 #endif
172         {
173             WARN("error read entry (%d)\n", errno);
174             /* update status according to new status */
175             CDAUDIO_GetCDStatus(wcda);
176
177             return FALSE;
178         }
179 #ifdef linux
180         start = CDFRAMES_PERSEC * (SECONDS_PERMIN * 
181                                    entry.cdte_addr.msf.minute + entry.cdte_addr.msf.second) + 
182             entry.cdte_addr.msf.frame;
183 #else
184         start = CDFRAMES_PERSEC * (SECONDS_PERMIN *
185                                    toc_buffer.addr.msf.minute + toc_buffer.addr.msf.second) +
186             toc_buffer.addr.msf.frame;
187 #endif
188         if (i == 0) {
189             last_start = start;
190             wcda->dwFirstOffset = start;
191             TRACE("dwFirstOffset=%u\n", start);
192         } else {
193             length = start - last_start;
194             last_start = start;
195             start = last_start - length;
196             total_length += length;
197             wcda->lpdwTrackLen[i - 1] = length;
198             wcda->lpdwTrackPos[i - 1] = start;
199             TRACE("track #%u start=%u len=%u\n", i, start, length);
200         }
201 #ifdef linux
202         wcda->lpbTrackFlags[i] =
203             (entry.cdte_adr << 4) | (entry.cdte_ctrl & 0x0f);
204 #else
205         wcda->lpbTrackFlags[i] =
206             (toc_buffer.addr_type << 4) | (toc_buffer.control & 0x0f);
207 #endif 
208         TRACE("track #%u flags=%02x\n", i + 1, wcda->lpbTrackFlags[i]);
209     }
210     wcda->dwTotalLen = total_length;
211     TRACE("total_len=%u\n", total_length);
212     return TRUE;
213 #else
214     return FALSE;
215 #endif
216 }
217
218 /**************************************************************************
219  *                              CDAUDIO_GetCDStatus             [internal]
220  */
221 BOOL CDAUDIO_GetCDStatus(WINE_CDAUDIO* wcda)
222 {
223 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
224     int         oldmode = wcda->cdaMode;
225 #ifdef linux
226     wcda->sc.cdsc_format = CDROM_MSF;
227 #else
228     struct ioc_read_subchannel  read_sc;
229     
230     read_sc.address_format = CD_MSF_FORMAT;
231     read_sc.data_format    = CD_CURRENT_POSITION;
232     read_sc.track          = 0;
233     read_sc.data_len       = sizeof(wcda->sc);
234     read_sc.data           = (struct cd_sub_channel_info *)&wcda->sc;
235 #endif
236 #ifdef linux
237     if (ioctl(wcda->unixdev, CDROMSUBCHNL, &wcda->sc))
238 #else
239     if (ioctl(wcda->unixdev, CDIOCREADSUBCHANNEL, &read_sc))
240 #endif
241     {
242         TRACE("opened or no_media (%d)!\n", errno);
243         wcda->cdaMode = WINE_CDA_OPEN; /* was NOT_READY */
244         return TRUE;
245     }
246     switch (
247 #ifdef linux
248             wcda->sc.cdsc_audiostatus
249 #else
250             wcda->sc.header.audio_status
251 #endif
252             ) {
253 #ifdef linux
254     case CDROM_AUDIO_INVALID:
255 #else
256     case CD_AS_AUDIO_INVALID:
257 #endif
258         WARN("device doesn't support status.\n");
259         wcda->cdaMode = WINE_CDA_DONTKNOW;
260         break;
261 #ifdef linux
262     case CDROM_AUDIO_NO_STATUS: 
263 #else
264     case CD_AS_NO_STATUS:
265 #endif
266         wcda->cdaMode = WINE_CDA_STOP;
267         TRACE("WINE_CDA_STOP !\n");
268         break;
269 #ifdef linux
270     case CDROM_AUDIO_PLAY: 
271 #else
272     case CD_AS_PLAY_IN_PROGRESS:
273 #endif
274         wcda->cdaMode = WINE_CDA_PLAY;
275         break;
276 #ifdef linux
277     case CDROM_AUDIO_PAUSED:
278 #else
279     case CD_AS_PLAY_PAUSED:
280 #endif
281         wcda->cdaMode = WINE_CDA_PAUSE;
282         TRACE("WINE_CDA_PAUSE !\n");
283         break;
284     default:
285 #ifdef linux
286         TRACE("status=%02X !\n",
287               wcda->sc.cdsc_audiostatus);
288 #else
289         TRACE("status=%02X !\n",
290               wcda->sc.header.audio_status);
291 #endif
292     }
293 #ifdef linux
294     wcda->nCurTrack = wcda->sc.cdsc_trk;
295     wcda->dwCurFrame = 
296         CDFRAMES_PERMIN * wcda->sc.cdsc_absaddr.msf.minute +
297         CDFRAMES_PERSEC * wcda->sc.cdsc_absaddr.msf.second +
298         wcda->sc.cdsc_absaddr.msf.frame;
299 #else
300     wcda->nCurTrack = wcda->sc.what.position.track_number;
301     wcda->dwCurFrame = 
302         CDFRAMES_PERMIN * wcda->sc.what.position.absaddr.msf.minute +
303         CDFRAMES_PERSEC * wcda->sc.what.position.absaddr.msf.second +
304         wcda->sc.what.position.absaddr.msf.frame;
305 #endif
306 #ifdef linux
307     TRACE("%02u-%02u:%02u:%02u \n",
308           wcda->sc.cdsc_trk,
309           wcda->sc.cdsc_absaddr.msf.minute,
310           wcda->sc.cdsc_absaddr.msf.second,
311           wcda->sc.cdsc_absaddr.msf.frame);
312 #else
313     TRACE("%02u-%02u:%02u:%02u \n",
314           wcda->sc.what.position.track_number,
315           wcda->sc.what.position.absaddr.msf.minute,
316           wcda->sc.what.position.absaddr.msf.second,
317           wcda->sc.what.position.absaddr.msf.frame);
318 #endif
319     
320     if (oldmode != wcda->cdaMode && oldmode == WINE_CDA_OPEN) {
321         if (!CDAUDIO_GetTracksInfo(wcda)) {
322             WARN("error updating TracksInfo !\n");
323             return FALSE;
324         }
325     }
326     return TRUE;
327 #else
328     return FALSE;
329 #endif
330 }
331
332 /**************************************************************************
333  *                              CDAUDIO_Play                    [internal]
334  */
335 int     CDAUDIO_Play(WINE_CDAUDIO* wcda, DWORD start, DWORD end)
336 {
337 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
338 #ifdef linux
339     struct      cdrom_msf       msf;
340 #else
341     struct      ioc_play_msf    msf;
342 #endif
343
344 #ifdef linux
345     msf.cdmsf_min0 = start / CDFRAMES_PERMIN;
346     msf.cdmsf_sec0 = (start % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
347     msf.cdmsf_frame0 = start % CDFRAMES_PERSEC;
348     msf.cdmsf_min1 = end / CDFRAMES_PERMIN;
349     msf.cdmsf_sec1 = (end % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
350     msf.cdmsf_frame1 = end % CDFRAMES_PERSEC;
351 #else
352     msf.start_m     = start / CDFRAMES_PERMIN;
353     msf.start_s     = (start % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
354     msf.start_f     = start % CDFRAMES_PERSEC;
355     msf.end_m       = end / CDFRAMES_PERMIN;
356     msf.end_s       = (end % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
357     msf.end_f       = end % CDFRAMES_PERSEC;
358 #endif
359 #ifdef linux
360     if (ioctl(wcda->unixdev, CDROMSTART))
361 #else
362     if (ioctl(wcda->unixdev, CDIOCSTART, NULL))
363 #endif
364     {
365         WARN("motor doesn't start !\n");
366         return -1;
367     }
368 #ifdef linux
369     if (ioctl(wcda->unixdev, CDROMPLAYMSF, &msf))
370 #else
371     if (ioctl(wcda->unixdev, CDIOCPLAYMSF, &msf))
372 #endif
373     {
374         WARN("device doesn't play !\n");
375         return -1;
376     }
377 #ifdef linux
378     TRACE("msf = %d:%d:%d %d:%d:%d\n",
379           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
380           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
381 #else
382     TRACE("msf = %d:%d:%d %d:%d:%d\n",
383           msf.start_m, msf.start_s, msf.start_f,
384           msf.end_m,   msf.end_s,   msf.end_f);
385 #endif
386     return 0;
387 #else
388     return -1;
389 #endif
390 }
391
392 /**************************************************************************
393  *                              CDAUDIO_Stop                    [internal]
394  */
395 int     CDAUDIO_Stop(WINE_CDAUDIO* wcda)
396 {
397 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
398     int ret = 0;
399 #ifdef linux
400     ret = ioctl(wcda->unixdev, CDROMSTOP);
401 #else
402     ret = ioctl(wcda->unixdev, CDIOCSTOP, NULL);
403 #endif
404     return ret;
405 #else
406     return -1;
407 #endif
408 }
409
410 /**************************************************************************
411  *                              CDAUDIO_Pause                   [internal]
412  */
413 int     CDAUDIO_Pause(WINE_CDAUDIO* wcda, int pauseOn)
414 {
415 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
416     int ret = 0;    
417 #ifdef linux
418     ret = ioctl(wcda->unixdev, pauseOn ? CDROMPAUSE : CDROMRESUME);
419 #else
420     ret = ioctl(wcda->unixdev, pauseOn ? CDIOCPAUSE : CDIOCRESUME, NULL);
421 #endif
422     return ret;
423 #else
424     return -1;
425 #endif
426 }
427
428 int     CDAUDIO_Seek(WINE_CDAUDIO* wcda, DWORD at)
429 {
430 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
431     int                         ret = 0;    
432 #ifdef linux
433     struct cdrom_msf0           msf;
434     msf.minute = at / CDFRAMES_PERMIN;
435     msf.second = (at % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
436     msf.frame  = at % CDFRAMES_PERSEC;
437
438    ret = ioctl(wcda->unixdev, CDROMSEEK, &msf);
439 #else
440    /* FIXME: the current end for play is lost 
441     * use end of CD ROM instead
442     */
443    FIXME("Could a BSD expert implement the seek function ?\n");
444    CDAUDIO_Play(wcda, at, wcda->lpdwTrackPos[wcda->nTracks] + wcda->lpdwTrackLen[wcda->nTracks]);
445    
446 #endif
447     return ret;
448 #else
449     return -1;
450 #endif
451 }
452
453 /**************************************************************************
454  *                              CDAUDIO_SetDoor                 [internal]
455  */
456 int     CDAUDIO_SetDoor(WINE_CDAUDIO* wcda, int open)
457 {
458 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
459     int ret = 0;    
460 #ifdef linux
461     if (open) {
462         ret = ioctl(wcda->unixdev, CDROMEJECT);
463     } else {
464         ret = ioctl(wcda->unixdev, CDROMEJECT, 1);
465     }
466 #else
467     ret = (ioctl(wcda->unixdev, CDIOCALLOW, NULL)) || 
468         (ioctl(wcda->unixdev, open ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
469         (ioctl(wcda->unixdev, CDIOCPREVENT, NULL));
470 #endif
471     wcda->nTracks = 0;
472     return ret;
473 #else
474     return -1;
475 #endif
476 }
477
478 /**************************************************************************
479  *                              CDAUDIO_Reset                   [internal]
480  */
481 int     CDAUDIO_Reset(WINE_CDAUDIO* wcda) 
482 {
483 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
484     int ret = 0;
485 #ifdef linux
486     ret = ioctl(wcda->unixdev, CDROMRESET);
487 #else
488     ret = ioctl(wcda->unixdev, CDIOCRESET, NULL);
489 #endif
490     return ret;
491 #else
492     return -1;
493 #endif
494 }
495