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