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