1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Main file for CD-ROM support
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1999 Eric Pouech
7 * Copyright 2000 Andreas Mohr
15 #include <sys/ioctl.h>
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(cdrom
);
22 #define MAX_CDAUDIO_TRACKS 256
24 /**************************************************************************
25 * CDROM_Open [internal]
28 * or -1 (figure it out)
30 int CDROM_Open(WINE_CDAUDIO
* wcda
, int drive
)
38 for (i
=0; i
< MAX_DOS_DRIVES
; i
++)
39 if (DRIVE_GetType(i
) == TYPE_CDROM
)
51 WARN("No CD-ROM #%d found !\n", drive
);
54 if ((dev
= DRIVE_GetDevice(drive
)) == NULL
)
56 WARN("No device entry for CD-ROM #%d (drive %c:) found !\n",
61 wcda
->unixdev
= open(dev
, O_RDONLY
| O_NONBLOCK
, 0);
62 if (wcda
->unixdev
== -1) {
63 WARN("can't open '%s'!. errno=%d\n", dev
, errno
);
66 wcda
->cdaMode
= WINE_CDA_OPEN
; /* to force reading tracks info */
69 wcda
->dwFirstFrame
= 0;
70 wcda
->dwLastFrame
= 0;
71 wcda
->lpdwTrackLen
= NULL
;
72 wcda
->lpdwTrackPos
= NULL
;
73 wcda
->lpbTrackFlags
= NULL
;
77 /**************************************************************************
78 * CDROM_GetMediaType [internal]
80 int CDROM_GetMediaType(WINE_CDAUDIO
* wcda
)
83 return ioctl(wcda
->unixdev
, CDROM_DISC_STATUS
);
89 /**************************************************************************
90 * CDROM_Close [internal]
92 int CDROM_Close(WINE_CDAUDIO
* wcda
)
94 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
95 if (wcda
->lpdwTrackLen
!= NULL
) free(wcda
->lpdwTrackLen
);
96 if (wcda
->lpdwTrackPos
!= NULL
) free(wcda
->lpdwTrackPos
);
97 if (wcda
->lpbTrackFlags
!= NULL
) free(wcda
->lpbTrackFlags
);
105 /**************************************************************************
106 * CDROM_Get_UPC [internal]
108 * upc has to be 14 bytes long
110 int CDROM_Get_UPC(WINE_CDAUDIO
* wcda
, LPSTR upc
)
113 struct cdrom_mcn mcn
;
114 int status
= ioctl(wcda
->unixdev
, CDROM_GET_MCN
, &mcn
);
117 ERR("ioctl() failed with code %d\n",status
);
120 strcpy(upc
, mcn
.medium_catalog_number
);
127 /**************************************************************************
128 * CDROM_Audio_GetNumberOfTracks [internal]
130 UINT16
CDROM_Audio_GetNumberOfTracks(WINE_CDAUDIO
* wcda
)
132 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
134 struct cdrom_tochdr hdr
;
136 struct ioc_toc_header hdr
;
139 if (wcda
->nTracks
== 0) {
141 if (ioctl(wcda
->unixdev
, CDROMREADTOCHDR
, &hdr
))
143 if (ioctl(wcda
->unixdev
, CDIOREADTOCHEADER
, &hdr
))
146 WARN("(%p) -- Error occurred (%d)!\n", wcda
, errno
);
150 wcda
->nFirstTrack
= hdr
.cdth_trk0
;
151 wcda
->nLastTrack
= hdr
.cdth_trk1
;
153 wcda
->nFirstTrack
= hdr
.starting_track
;
154 wcda
->nLastTrack
= hdr
.ending_track
;
156 wcda
->nTracks
= wcda
->nLastTrack
- wcda
->nFirstTrack
+ 1;
158 return wcda
->nTracks
;
164 /**************************************************************************
165 * CDROM_Audio_GetTracksInfo [internal]
167 BOOL
CDROM_Audio_GetTracksInfo(WINE_CDAUDIO
* wcda
)
169 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
171 int start
, last_start
= 0;
172 int total_length
= 0;
174 struct cdrom_tocentry entry
;
176 struct ioc_read_toc_entry entry
;
177 struct cd_toc_entry toc_buffer
;
180 if (wcda
->nTracks
== 0) {
181 if (CDROM_Audio_GetNumberOfTracks(wcda
) == (WORD
)-1) return FALSE
;
183 TRACE("nTracks=%u\n", wcda
->nTracks
);
185 if (wcda
->lpdwTrackLen
!= NULL
)
186 free(wcda
->lpdwTrackLen
);
187 wcda
->lpdwTrackLen
= (LPDWORD
)malloc((wcda
->nTracks
+ 1) * sizeof(DWORD
));
188 if (wcda
->lpdwTrackPos
!= NULL
)
189 free(wcda
->lpdwTrackPos
);
190 wcda
->lpdwTrackPos
= (LPDWORD
)malloc((wcda
->nTracks
+ 1) * sizeof(DWORD
));
191 if (wcda
->lpbTrackFlags
!= NULL
)
192 free(wcda
->lpbTrackFlags
);
193 wcda
->lpbTrackFlags
= (LPBYTE
)malloc((wcda
->nTracks
+ 1) * sizeof(BYTE
));
194 if (wcda
->lpdwTrackLen
== NULL
|| wcda
->lpdwTrackPos
== NULL
||
195 wcda
->lpbTrackFlags
== NULL
) {
196 WARN("error allocating track table !\n");
199 memset(wcda
->lpdwTrackLen
, 0, (wcda
->nTracks
+ 1) * sizeof(DWORD
));
200 memset(wcda
->lpdwTrackPos
, 0, (wcda
->nTracks
+ 1) * sizeof(DWORD
));
201 memset(wcda
->lpbTrackFlags
, 0, (wcda
->nTracks
+ 1) * sizeof(BYTE
));
202 for (i
= 0; i
<= wcda
->nTracks
; i
++) {
203 if (i
== wcda
->nTracks
)
205 entry
.cdte_track
= CDROM_LEADOUT
;
208 entry
.starting_track
= LEADOUT
; /* XXX */
212 entry
.cdte_track
= i
+ 1;
214 entry
.starting_track
= i
+ 1;
217 entry
.cdte_format
= CDROM_MSF
;
219 bzero((char *)&toc_buffer
, sizeof(toc_buffer
));
220 entry
.address_format
= CD_MSF_FORMAT
;
221 entry
.data_len
= sizeof(toc_buffer
);
222 entry
.data
= &toc_buffer
;
225 if (ioctl(wcda
->unixdev
, CDROMREADTOCENTRY
, &entry
))
227 if (ioctl(wcda
->unixdev
, CDIOREADTOCENTRYS
, &entry
))
230 WARN("error read entry (%d)\n", errno
);
231 /* update status according to new status */
232 CDROM_Audio_GetCDStatus(wcda
);
237 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
238 entry
.cdte_addr
.msf
.minute
+ entry
.cdte_addr
.msf
.second
) +
239 entry
.cdte_addr
.msf
.frame
;
241 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
242 toc_buffer
.addr
.msf
.minute
+ toc_buffer
.addr
.msf
.second
) +
243 toc_buffer
.addr
.msf
.frame
;
247 wcda
->dwFirstFrame
= start
;
248 TRACE("dwFirstOffset=%u\n", start
);
250 length
= start
- last_start
;
252 start
= last_start
- length
;
253 total_length
+= length
;
254 wcda
->lpdwTrackLen
[i
- 1] = length
;
255 wcda
->lpdwTrackPos
[i
- 1] = start
;
256 TRACE("track #%u start=%u len=%u\n", i
, start
, length
);
259 wcda
->lpbTrackFlags
[i
] =
260 (entry
.cdte_adr
<< 4) | (entry
.cdte_ctrl
& 0x0f);
262 wcda
->lpbTrackFlags
[i
] =
263 (toc_buffer
.addr_type
<< 4) | (toc_buffer
.control
& 0x0f);
265 TRACE("track #%u flags=%02x\n", i
+ 1, wcda
->lpbTrackFlags
[i
]);
267 wcda
->dwLastFrame
= last_start
;
268 TRACE("total_len=%u\n", total_length
);
275 /**************************************************************************
276 * CDROM_Audio_GetCDStatus [internal]
278 BOOL
CDROM_Audio_GetCDStatus(WINE_CDAUDIO
* wcda
)
280 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
281 int oldmode
= wcda
->cdaMode
;
283 wcda
->sc
.cdsc_format
= CDROM_MSF
;
285 struct ioc_read_subchannel read_sc
;
287 read_sc
.address_format
= CD_MSF_FORMAT
;
288 read_sc
.data_format
= CD_CURRENT_POSITION
;
290 read_sc
.data_len
= sizeof(wcda
->sc
);
291 read_sc
.data
= (struct cd_sub_channel_info
*)&wcda
->sc
;
294 if (ioctl(wcda
->unixdev
, CDROMSUBCHNL
, &wcda
->sc
))
296 if (ioctl(wcda
->unixdev
, CDIOCREADSUBCHANNEL
, &read_sc
))
299 TRACE("opened or no_media (%d)!\n", errno
);
300 wcda
->cdaMode
= WINE_CDA_OPEN
; /* was NOT_READY */
305 wcda
->sc
.cdsc_audiostatus
307 wcda
->sc
.header
.audio_status
311 case CDROM_AUDIO_INVALID
:
313 case CD_AS_AUDIO_INVALID
:
315 WARN("device doesn't support status.\n");
316 wcda
->cdaMode
= WINE_CDA_DONTKNOW
;
319 case CDROM_AUDIO_NO_STATUS
:
321 case CD_AS_NO_STATUS
:
323 wcda
->cdaMode
= WINE_CDA_STOP
;
324 TRACE("WINE_CDA_STOP !\n");
327 case CDROM_AUDIO_PLAY
:
329 case CD_AS_PLAY_IN_PROGRESS
:
331 wcda
->cdaMode
= WINE_CDA_PLAY
;
334 case CDROM_AUDIO_PAUSED
:
336 case CD_AS_PLAY_PAUSED
:
338 wcda
->cdaMode
= WINE_CDA_PAUSE
;
339 TRACE("WINE_CDA_PAUSE !\n");
343 TRACE("status=%02X !\n",
344 wcda
->sc
.cdsc_audiostatus
);
346 TRACE("status=%02X !\n",
347 wcda
->sc
.header
.audio_status
);
351 wcda
->nCurTrack
= wcda
->sc
.cdsc_trk
;
353 CDFRAMES_PERMIN
* wcda
->sc
.cdsc_absaddr
.msf
.minute
+
354 CDFRAMES_PERSEC
* wcda
->sc
.cdsc_absaddr
.msf
.second
+
355 wcda
->sc
.cdsc_absaddr
.msf
.frame
;
357 wcda
->nCurTrack
= wcda
->sc
.what
.position
.track_number
;
359 CDFRAMES_PERMIN
* wcda
->sc
.what
.position
.absaddr
.msf
.minute
+
360 CDFRAMES_PERSEC
* wcda
->sc
.what
.position
.absaddr
.msf
.second
+
361 wcda
->sc
.what
.position
.absaddr
.msf
.frame
;
364 TRACE("%02u-%02u:%02u:%02u \n",
366 wcda
->sc
.cdsc_absaddr
.msf
.minute
,
367 wcda
->sc
.cdsc_absaddr
.msf
.second
,
368 wcda
->sc
.cdsc_absaddr
.msf
.frame
);
370 TRACE("%02u-%02u:%02u:%02u \n",
371 wcda
->sc
.what
.position
.track_number
,
372 wcda
->sc
.what
.position
.absaddr
.msf
.minute
,
373 wcda
->sc
.what
.position
.absaddr
.msf
.second
,
374 wcda
->sc
.what
.position
.absaddr
.msf
.frame
);
377 if (oldmode
!= wcda
->cdaMode
&& oldmode
== WINE_CDA_OPEN
) {
378 if (!CDROM_Audio_GetTracksInfo(wcda
)) {
379 WARN("error updating TracksInfo !\n");
389 /**************************************************************************
390 * CDROM_Audio_Play [internal]
392 int CDROM_Audio_Play(WINE_CDAUDIO
* wcda
, DWORD start
, DWORD end
)
394 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
396 struct cdrom_msf msf
;
398 struct ioc_play_msf msf
;
402 msf
.cdmsf_min0
= start
/ CDFRAMES_PERMIN
;
403 msf
.cdmsf_sec0
= (start
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
404 msf
.cdmsf_frame0
= start
% CDFRAMES_PERSEC
;
405 msf
.cdmsf_min1
= end
/ CDFRAMES_PERMIN
;
406 msf
.cdmsf_sec1
= (end
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
407 msf
.cdmsf_frame1
= end
% CDFRAMES_PERSEC
;
409 msf
.start_m
= start
/ CDFRAMES_PERMIN
;
410 msf
.start_s
= (start
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
411 msf
.start_f
= start
% CDFRAMES_PERSEC
;
412 msf
.end_m
= end
/ CDFRAMES_PERMIN
;
413 msf
.end_s
= (end
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
414 msf
.end_f
= end
% CDFRAMES_PERSEC
;
417 if (ioctl(wcda
->unixdev
, CDROMSTART
))
419 if (ioctl(wcda
->unixdev
, CDIOCSTART
, NULL
))
422 WARN("motor doesn't start !\n");
426 if (ioctl(wcda
->unixdev
, CDROMPLAYMSF
, &msf
))
428 if (ioctl(wcda
->unixdev
, CDIOCPLAYMSF
, &msf
))
431 WARN("device doesn't play !\n");
435 TRACE("msf = %d:%d:%d %d:%d:%d\n",
436 msf
.cdmsf_min0
, msf
.cdmsf_sec0
, msf
.cdmsf_frame0
,
437 msf
.cdmsf_min1
, msf
.cdmsf_sec1
, msf
.cdmsf_frame1
);
439 TRACE("msf = %d:%d:%d %d:%d:%d\n",
440 msf
.start_m
, msf
.start_s
, msf
.start_f
,
441 msf
.end_m
, msf
.end_s
, msf
.end_f
);
449 /**************************************************************************
450 * CDROM_Audio_Stop [internal]
452 int CDROM_Audio_Stop(WINE_CDAUDIO
* wcda
)
454 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
457 ret
= ioctl(wcda
->unixdev
, CDROMSTOP
);
459 ret
= ioctl(wcda
->unixdev
, CDIOCSTOP
, NULL
);
467 /**************************************************************************
468 * CDROM_Audio_Pause [internal]
470 int CDROM_Audio_Pause(WINE_CDAUDIO
* wcda
, int pauseOn
)
472 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
475 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDROMPAUSE
: CDROMRESUME
);
477 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDIOCPAUSE
: CDIOCRESUME
, NULL
);
485 /**************************************************************************
486 * CDROM_Audio_Seek [internal]
488 int CDROM_Audio_Seek(WINE_CDAUDIO
* wcda
, DWORD at
)
490 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
493 struct cdrom_msf0 msf
;
494 msf
.minute
= at
/ CDFRAMES_PERMIN
;
495 msf
.second
= (at
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
496 msf
.frame
= at
% CDFRAMES_PERSEC
;
498 ret
= ioctl(wcda
->unixdev
, CDROMSEEK
, &msf
);
500 /* FIXME: the current end for play is lost
501 * use end of CD ROM instead
503 FIXME("Could a BSD expert implement the seek function ?\n");
504 CDROM_Audio_Play(wcda
, at
, wcda
->lpdwTrackPos
[wcda
->nTracks
] + wcda
->lpdwTrackLen
[wcda
->nTracks
]);
513 /**************************************************************************
514 * CDROM_SetDoor [internal]
516 int CDROM_SetDoor(WINE_CDAUDIO
* wcda
, int open
)
518 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
522 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
);
524 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
, 1);
527 ret
= (ioctl(wcda
->unixdev
, CDIOCALLOW
, NULL
)) ||
528 (ioctl(wcda
->unixdev
, open
? CDIOCEJECT
: CDIOCCLOSE
, NULL
)) ||
529 (ioctl(wcda
->unixdev
, CDIOCPREVENT
, NULL
));
538 /**************************************************************************
539 * CDROM_Reset [internal]
541 int CDROM_Reset(WINE_CDAUDIO
* wcda
)
543 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
546 ret
= ioctl(wcda
->unixdev
, CDROMRESET
);
548 ret
= ioctl(wcda
->unixdev
, CDIOCRESET
, NULL
);
556 unsigned int get_offs_best_voldesc(int fd
)
558 BYTE cur_vd_type
, max_vd_type
= 0;
559 unsigned int offs
, best_offs
= 0;
561 for (offs
=0x8000; offs
<= 0x9800; offs
+= 0x800)
563 lseek(fd
, offs
, SEEK_SET
);
564 read(fd
, &cur_vd_type
, 1);
565 if (cur_vd_type
== 0xff)
567 if (cur_vd_type
> max_vd_type
)
569 max_vd_type
= cur_vd_type
;
576 /**************************************************************************
577 * CDROM_Audio_GetSerial [internal]
579 DWORD
CDROM_Audio_GetSerial(WINE_CDAUDIO
* wcda
)
581 unsigned long serial
= 0;
584 WORD wMinutes
, wSeconds
, wFrames
;
586 for (i
= 0; i
< wcda
->nTracks
; i
++) {
587 dwFrame
= wcda
->lpdwTrackPos
[i
];
588 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
589 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
590 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
591 msf
= CDROM_MAKE_MSF(wMinutes
, wSeconds
, wFrames
);
593 serial
+= (CDROM_MSF_MINUTE(msf
) << 16) +
594 (CDROM_MSF_SECOND(msf
) << 8) +
595 (CDROM_MSF_FRAME(msf
));
600 /**************************************************************************
601 * CDROM_Data_GetSerial [internal]
603 DWORD
CDROM_Data_GetSerial(WINE_CDAUDIO
* wcda
)
605 unsigned int offs
= get_offs_best_voldesc(wcda
->unixdev
);
617 lseek(wcda
->unixdev
,offs
,SEEK_SET
);
618 read(wcda
->unixdev
,buf
,2048);
619 for(i
=0; i
<2048; i
+=4)
621 /* DON'T optimize this into DWORD !! (breaks overflow) */
622 serial
.p
[0] += buf
[i
+0];
623 serial
.p
[1] += buf
[i
+1];
624 serial
.p
[2] += buf
[i
+2];
625 serial
.p
[3] += buf
[i
+3];
631 /**************************************************************************
632 * CDROM_GetSerial [internal]
634 DWORD
CDROM_GetSerial(int drive
)
639 /* EXPIRES 01.01.2001 */
640 FIXME("CD-ROM serial number calculation might fail.\n");
641 FIXME("Please test with as many exotic CDs as possible !\n");
643 if (!(CDROM_Open(&wcda
, drive
)))
645 int media
= CDROM_GetMediaType(&wcda
);
648 if (media
== CDS_AUDIO
)
650 if (!(CDROM_Audio_GetCDStatus(&wcda
))) {
651 ERR("couldn't get CD status !\n");
655 serial
= CDROM_Audio_GetSerial(&wcda
);
658 if (media
> CDS_AUDIO
)
659 /* hopefully a data CD */
660 serial
= CDROM_Data_GetSerial(&wcda
);
662 p
= (media
== CDS_AUDIO
) ? "Audio " :
663 (media
> CDS_AUDIO
) ? "Data " : "";
665 FIXME("%sCD serial number is %04x-%04x.\n",
666 p
, HIWORD(serial
), LOWORD(serial
));
668 ERR("couldn't get %sCD serial !\n", p
);