1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample CDAUDIO Wine Driver for Linux
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1999 Eric Pouech
12 #include <sys/ioctl.h>
16 DEFAULT_DEBUG_CHANNEL(cdaudio
)
18 #if defined(__NetBSD__)
19 # define CDAUDIO_DEV "/dev/rcd0d"
20 #elif defined(__FreeBSD__)
21 # define CDAUDIO_DEV "/dev/rcd0c"
23 # define CDAUDIO_DEV "/dev/cdrom"
26 #define MAX_CDAUDIO_TRACKS 256
28 /**************************************************************************
29 * CDAUDIO_Open [internal]
31 int CDAUDIO_Open(WINE_CDAUDIO
* wcda
)
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(cdaudio
,"can't open '%s'!. errno=%d\n", CDAUDIO_DEV
, errno
);
39 wcda
->cdaMode
= WINE_CDA_OPEN
; /* to force reading tracks info */
43 wcda
->dwFirstOffset
= 0;
44 wcda
->lpdwTrackLen
= NULL
;
45 wcda
->lpdwTrackPos
= NULL
;
46 wcda
->lpbTrackFlags
= NULL
;
54 /**************************************************************************
55 * CDAUDIO_Close [internal]
57 int CDAUDIO_Close(WINE_CDAUDIO
* wcda
)
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
);
70 /**************************************************************************
71 * CDAUDIO_GetNumberOfTracks [internal]
73 UINT16
CDAUDIO_GetNumberOfTracks(WINE_CDAUDIO
* wcda
)
75 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
77 struct cdrom_tochdr hdr
;
79 struct ioc_toc_header hdr
;
82 if (wcda
->nTracks
== 0) {
84 if (ioctl(wcda
->unixdev
, CDROMREADTOCHDR
, &hdr
))
86 if (ioctl(wcda
->unixdev
, CDIOREADTOCHEADER
, &hdr
))
89 WARN(cdaudio
, "(%p) -- Error occured (%d)!\n", wcda
, errno
);
93 wcda
->nFirstTrack
= hdr
.cdth_trk0
;
94 wcda
->nLastTrack
= hdr
.cdth_trk1
;
96 wcda
->nFirstTrack
= hdr
.starting_track
;
97 wcda
->nLastTrack
= hdr
.ending_track
;
99 wcda
->nTracks
= wcda
->nLastTrack
- wcda
->nFirstTrack
+ 1;
101 return wcda
->nTracks
;
107 /**************************************************************************
108 * CDAUDIO_GetTracksInfo [internal]
110 BOOL
CDAUDIO_GetTracksInfo(WINE_CDAUDIO
* wcda
)
112 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
114 int start
, last_start
= 0;
115 int total_length
= 0;
117 struct cdrom_tocentry entry
;
119 struct ioc_read_toc_entry entry
;
120 struct cd_toc_entry toc_buffer
;
123 if (wcda
->nTracks
== 0) {
124 if (CDAUDIO_GetNumberOfTracks(wcda
) == (WORD
)-1) return FALSE
;
126 TRACE(cdaudio
,"nTracks=%u\n", wcda
->nTracks
);
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(cdaudio
, "error allocating track table !\n");
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
)
148 entry
.cdte_track
= CDROM_LEADOUT
;
151 entry
.starting_track
= LEADOUT
; /* XXX */
155 entry
.cdte_track
= i
+ 1;
157 entry
.starting_track
= i
+ 1;
160 entry
.cdte_format
= CDROM_MSF
;
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
;
168 if (ioctl(wcda
->unixdev
, CDROMREADTOCENTRY
, &entry
))
170 if (ioctl(wcda
->unixdev
, CDIOREADTOCENTRYS
, &entry
))
173 WARN(cdaudio
, "error read entry (%d)\n", errno
);
174 /* update status according to new status */
175 CDAUDIO_GetCDStatus(wcda
);
180 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
181 entry
.cdte_addr
.msf
.minute
+ entry
.cdte_addr
.msf
.second
) +
182 entry
.cdte_addr
.msf
.frame
;
184 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
185 toc_buffer
.addr
.msf
.minute
+ toc_buffer
.addr
.msf
.second
) +
186 toc_buffer
.addr
.msf
.frame
;
190 wcda
->dwFirstOffset
= start
;
191 TRACE(cdaudio
, "dwFirstOffset=%u\n", start
);
193 length
= start
- last_start
;
195 start
= last_start
- length
;
196 total_length
+= length
;
197 wcda
->lpdwTrackLen
[i
- 1] = length
;
198 wcda
->lpdwTrackPos
[i
- 1] = start
;
199 TRACE(cdaudio
, "track #%u start=%u len=%u\n", i
, start
, length
);
202 wcda
->lpbTrackFlags
[i
] =
203 (entry
.cdte_adr
<< 4) | (entry
.cdte_ctrl
& 0x0f);
205 wcda
->lpbTrackFlags
[i
] =
206 (toc_buffer
.addr_type
<< 4) | (toc_buffer
.control
& 0x0f);
208 TRACE(cdaudio
, "track #%u flags=%02x\n", i
+ 1, wcda
->lpbTrackFlags
[i
]);
210 wcda
->dwTotalLen
= total_length
;
211 TRACE(cdaudio
,"total_len=%u\n", total_length
);
218 /**************************************************************************
219 * CDAUDIO_GetCDStatus [internal]
221 BOOL
CDAUDIO_GetCDStatus(WINE_CDAUDIO
* wcda
)
223 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
224 int oldmode
= wcda
->cdaMode
;
226 wcda
->sc
.cdsc_format
= CDROM_MSF
;
228 struct ioc_read_subchannel read_sc
;
230 read_sc
.address_format
= CD_MSF_FORMAT
;
231 read_sc
.data_format
= CD_CURRENT_POSITION
;
233 read_sc
.data_len
= sizeof(wcda
->sc
);
234 read_sc
.data
= (struct cd_sub_channel_info
*)&wcda
->sc
;
237 if (ioctl(wcda
->unixdev
, CDROMSUBCHNL
, &wcda
->sc
))
239 if (ioctl(wcda
->unixdev
, CDIOCREADSUBCHANNEL
, &read_sc
))
242 TRACE(cdaudio
,"opened or no_media (%d)!\n", errno
);
243 wcda
->cdaMode
= WINE_CDA_OPEN
; /* was NOT_READY */
248 wcda
->sc
.cdsc_audiostatus
250 wcda
->sc
.header
.audio_status
254 case CDROM_AUDIO_INVALID
:
256 case CD_AS_AUDIO_INVALID
:
258 WARN(cdaudio
, "device doesn't support status.\n");
259 wcda
->cdaMode
= WINE_CDA_DONTKNOW
;
262 case CDROM_AUDIO_NO_STATUS
:
264 case CD_AS_NO_STATUS
:
266 wcda
->cdaMode
= WINE_CDA_STOP
;
267 TRACE(cdaudio
,"WINE_CDA_STOP !\n");
270 case CDROM_AUDIO_PLAY
:
272 case CD_AS_PLAY_IN_PROGRESS
:
274 wcda
->cdaMode
= WINE_CDA_PLAY
;
277 case CDROM_AUDIO_PAUSED
:
279 case CD_AS_PLAY_PAUSED
:
281 wcda
->cdaMode
= WINE_CDA_PAUSE
;
282 TRACE(cdaudio
,"WINE_CDA_PAUSE !\n");
286 TRACE(cdaudio
,"status=%02X !\n",
287 wcda
->sc
.cdsc_audiostatus
);
289 TRACE(cdaudio
,"status=%02X !\n",
290 wcda
->sc
.header
.audio_status
);
294 wcda
->nCurTrack
= wcda
->sc
.cdsc_trk
;
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
;
300 wcda
->nCurTrack
= wcda
->sc
.what
.position
.track_number
;
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
;
307 TRACE(cdaudio
,"%02u-%02u:%02u:%02u \n",
309 wcda
->sc
.cdsc_absaddr
.msf
.minute
,
310 wcda
->sc
.cdsc_absaddr
.msf
.second
,
311 wcda
->sc
.cdsc_absaddr
.msf
.frame
);
313 TRACE(cdaudio
,"%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
);
320 if (oldmode
!= wcda
->cdaMode
&& oldmode
== WINE_CDA_OPEN
) {
321 if (!CDAUDIO_GetTracksInfo(wcda
)) {
322 WARN(cdaudio
, "error updating TracksInfo !\n");
332 /**************************************************************************
333 * CDAUDIO_Play [internal]
335 int CDAUDIO_Play(WINE_CDAUDIO
* wcda
, DWORD start
, DWORD end
)
337 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
339 struct cdrom_msf msf
;
341 struct ioc_play_msf msf
;
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
;
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
;
360 if (ioctl(wcda
->unixdev
, CDROMSTART
))
362 if (ioctl(wcda
->unixdev
, CDIOCSTART
, NULL
))
365 WARN(cdaudio
, "motor doesn't start !\n");
369 if (ioctl(wcda
->unixdev
, CDROMPLAYMSF
, &msf
))
371 if (ioctl(wcda
->unixdev
, CDIOCPLAYMSF
, &msf
))
374 WARN(cdaudio
, "device doesn't play !\n");
378 TRACE(cdaudio
,"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
);
382 TRACE(cdaudio
,"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
);
392 /**************************************************************************
393 * CDAUDIO_Stop [internal]
395 int CDAUDIO_Stop(WINE_CDAUDIO
* wcda
)
397 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
400 ret
= ioctl(wcda
->unixdev
, CDROMSTOP
);
402 ret
= ioctl(wcda
->unixdev
, CDIOCSTOP
, NULL
);
410 /**************************************************************************
411 * CDAUDIO_Pause [internal]
413 int CDAUDIO_Pause(WINE_CDAUDIO
* wcda
, int pauseOn
)
415 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
418 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDROMPAUSE
: CDROMRESUME
);
420 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDIOCPAUSE
: CDIOCRESUME
, NULL
);
428 int CDAUDIO_Seek(WINE_CDAUDIO
* wcda
, DWORD at
)
430 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
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
;
438 ret
= ioctl(wcda
->unixdev
, CDROMSEEK
, &msf
);
440 /* FIXME: the current end for play is lost
441 * use end of CD ROM instead
443 FIXME(cdaudio
, "Could a BSD expert implement the seek function ?\n");
444 CDAUDIO_Play(wcda
, at
, wcda
->lpdwTrackPos
[wcda
->nTracks
] + wcda
->lpdwTrackLen
[wcda
->nTracks
]);
453 /**************************************************************************
454 * CDAUDIO_SetDoor [internal]
456 int CDAUDIO_SetDoor(WINE_CDAUDIO
* wcda
, int open
)
458 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
462 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
);
464 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
, 1);
467 ret
= (ioctl(wcda
->unixdev
, CDIOCALLOW
, NULL
)) ||
468 (ioctl(wcda
->unixdev
, open
? CDIOCEJECT
: CDIOCCLOSE
, NULL
)) ||
469 (ioctl(wcda
->unixdev
, CDIOCPREVENT
, NULL
));
478 /**************************************************************************
479 * CDAUDIO_Reset [internal]
481 int CDAUDIO_Reset(WINE_CDAUDIO
* wcda
)
483 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
486 ret
= ioctl(wcda
->unixdev
, CDROMRESET
);
488 ret
= ioctl(wcda
->unixdev
, CDIOCRESET
, NULL
);