Alias NMUPDOWN to be NM_UPDOWN, rather than overwrite NM_UPDOWN with
[wine.git] / misc / cdrom.c
blob2f2da7d8f88a015d2d13cfaeb97b9fcfa5841aea
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Main file for CD-ROM support
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1999 Eric Pouech
7 * Copyright 2000 Andreas Mohr
8 */
10 #include "config.h"
12 #include <errno.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <sys/ioctl.h>
16 #include "cdrom.h"
17 #include "drive.h"
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(cdrom);
22 #define MAX_CDAUDIO_TRACKS 256
24 /**************************************************************************
25 * CDROM_Open [internal]
27 * drive = 0, 1, ...
28 * or -1 (figure it out)
30 int CDROM_Open(WINE_CDAUDIO* wcda, int drive)
32 int i;
33 BOOL avail = FALSE;
34 const char *dev;
36 if (drive == -1)
38 for (i=0; i < MAX_DOS_DRIVES; i++)
39 if (DRIVE_GetType(i) == TYPE_CDROM)
41 drive = i;
42 avail = TRUE;
43 break;
46 else
47 avail = TRUE;
49 if (avail == FALSE)
51 WARN("No CD-ROM #%d found !\n", drive);
52 return -1;
54 if ((dev = DRIVE_GetDevice(drive)) == NULL)
56 WARN("No device entry for CD-ROM #%d (drive %c:) found !\n",
57 drive, 'A' + drive);
58 return -1;
61 wcda->unixdev = open(dev, O_RDONLY | O_NONBLOCK, 0);
62 if (wcda->unixdev == -1) {
63 WARN("can't open '%s'!. %s\n", dev, strerror(errno));
64 return -1;
66 wcda->cdaMode = WINE_CDA_OPEN; /* to force reading tracks info */
67 wcda->nCurTrack = 0;
68 wcda->nTracks = 0;
69 wcda->dwFirstFrame = 0;
70 wcda->dwLastFrame = 0;
71 wcda->lpdwTrackLen = NULL;
72 wcda->lpdwTrackPos = NULL;
73 wcda->lpbTrackFlags = NULL;
74 return 0;
77 /**************************************************************************
78 * CDROM_GetMediaType [internal]
80 int CDROM_GetMediaType(WINE_CDAUDIO* wcda)
82 #ifdef linux
83 return ioctl(wcda->unixdev, CDROM_DISC_STATUS);
84 #else
85 return -1;
86 #endif
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);
98 close(wcda->unixdev);
99 return 0;
100 #else
101 return -1;
102 #endif
105 /**************************************************************************
106 * CDROM_Get_UPC [internal]
108 * upc has to be 14 bytes long
110 int CDROM_Get_UPC(WINE_CDAUDIO* wcda, LPSTR upc)
112 #ifdef linux
113 struct cdrom_mcn mcn;
114 int status = ioctl(wcda->unixdev, CDROM_GET_MCN, &mcn);
115 if (status)
117 ERR("ioctl() failed with code %d\n",status);
118 return -1;
120 strcpy(upc, mcn.medium_catalog_number);
121 return 0;
122 #else
123 return -1;
124 #endif
127 /**************************************************************************
128 * CDROM_Audio_GetNumberOfTracks [internal]
130 UINT16 CDROM_Audio_GetNumberOfTracks(WINE_CDAUDIO* wcda)
132 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
133 #ifdef linux
134 struct cdrom_tochdr hdr;
135 #else
136 struct ioc_toc_header hdr;
137 #endif
139 if (wcda->nTracks == 0) {
140 #ifdef linux
141 if (ioctl(wcda->unixdev, CDROMREADTOCHDR, &hdr))
142 #else
143 if (ioctl(wcda->unixdev, CDIOREADTOCHEADER, &hdr))
144 #endif
146 WARN("(%p) -- Error occurred (%d)!\n", wcda, errno);
147 return (WORD)-1;
149 #ifdef linux
150 wcda->nFirstTrack = hdr.cdth_trk0;
151 wcda->nLastTrack = hdr.cdth_trk1;
152 #else
153 wcda->nFirstTrack = hdr.starting_track;
154 wcda->nLastTrack = hdr.ending_track;
155 #endif
156 wcda->nTracks = wcda->nLastTrack - wcda->nFirstTrack + 1;
158 return wcda->nTracks;
159 #else
160 return (WORD)-1;
161 #endif
164 /**************************************************************************
165 * CDROM_Audio_GetTracksInfo [internal]
167 BOOL CDROM_Audio_GetTracksInfo(WINE_CDAUDIO* wcda)
169 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
170 int i, length;
171 int start, last_start = 0;
172 int total_length = 0;
173 #ifdef linux
174 struct cdrom_tocentry entry;
175 #else
176 struct ioc_read_toc_entry entry;
177 struct cd_toc_entry toc_buffer;
178 #endif
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");
197 return FALSE;
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)
204 #ifdef linux
205 entry.cdte_track = CDROM_LEADOUT;
206 #else
207 #define LEADOUT 0xaa
208 entry.starting_track = LEADOUT; /* XXX */
209 #endif
210 else
211 #ifdef linux
212 entry.cdte_track = i + 1;
213 #else
214 entry.starting_track = i + 1;
215 #endif
216 #ifdef linux
217 entry.cdte_format = CDROM_MSF;
218 #else
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;
223 #endif
224 #ifdef linux
225 if (ioctl(wcda->unixdev, CDROMREADTOCENTRY, &entry))
226 #else
227 if (ioctl(wcda->unixdev, CDIOREADTOCENTRYS, &entry))
228 #endif
230 WARN("error read entry (%s)\n", strerror(errno));
231 /* update status according to new status */
232 CDROM_Audio_GetCDStatus(wcda);
234 return FALSE;
236 #ifdef linux
237 start = CDFRAMES_PERSEC * (SECONDS_PERMIN *
238 entry.cdte_addr.msf.minute + entry.cdte_addr.msf.second) +
239 entry.cdte_addr.msf.frame;
240 #else
241 start = CDFRAMES_PERSEC * (SECONDS_PERMIN *
242 toc_buffer.addr.msf.minute + toc_buffer.addr.msf.second) +
243 toc_buffer.addr.msf.frame;
244 #endif
245 if (i == 0) {
246 last_start = start;
247 wcda->dwFirstFrame = start;
248 TRACE("dwFirstOffset=%u\n", start);
249 } else {
250 length = start - last_start;
251 last_start = 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);
258 #ifdef linux
259 wcda->lpbTrackFlags[i] =
260 (entry.cdte_adr << 4) | (entry.cdte_ctrl & 0x0f);
261 #else
262 wcda->lpbTrackFlags[i] =
263 (toc_buffer.addr_type << 4) | (toc_buffer.control & 0x0f);
264 #endif
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);
269 return TRUE;
270 #else
271 return FALSE;
272 #endif
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;
282 #ifdef linux
283 wcda->sc.cdsc_format = CDROM_MSF;
284 #else
285 struct ioc_read_subchannel read_sc;
287 read_sc.address_format = CD_MSF_FORMAT;
288 read_sc.data_format = CD_CURRENT_POSITION;
289 read_sc.track = 0;
290 read_sc.data_len = sizeof(wcda->sc);
291 read_sc.data = (struct cd_sub_channel_info *)&wcda->sc;
292 #endif
293 #ifdef linux
294 if (ioctl(wcda->unixdev, CDROMSUBCHNL, &wcda->sc))
295 #else
296 if (ioctl(wcda->unixdev, CDIOCREADSUBCHANNEL, &read_sc))
297 #endif
299 TRACE("opened or no_media (%s)!\n", strerror(errno));
300 wcda->cdaMode = WINE_CDA_OPEN; /* was NOT_READY */
301 return TRUE;
303 switch (
304 #ifdef linux
305 wcda->sc.cdsc_audiostatus
306 #else
307 wcda->sc.header.audio_status
308 #endif
310 #ifdef linux
311 case CDROM_AUDIO_INVALID:
312 #else
313 case CD_AS_AUDIO_INVALID:
314 #endif
315 WARN("device doesn't support status.\n");
316 wcda->cdaMode = WINE_CDA_DONTKNOW;
317 break;
318 #ifdef linux
319 case CDROM_AUDIO_NO_STATUS:
320 #else
321 case CD_AS_NO_STATUS:
322 #endif
323 wcda->cdaMode = WINE_CDA_STOP;
324 TRACE("WINE_CDA_STOP !\n");
325 break;
326 #ifdef linux
327 case CDROM_AUDIO_PLAY:
328 #else
329 case CD_AS_PLAY_IN_PROGRESS:
330 #endif
331 wcda->cdaMode = WINE_CDA_PLAY;
332 break;
333 #ifdef linux
334 case CDROM_AUDIO_PAUSED:
335 #else
336 case CD_AS_PLAY_PAUSED:
337 #endif
338 wcda->cdaMode = WINE_CDA_PAUSE;
339 TRACE("WINE_CDA_PAUSE !\n");
340 break;
341 default:
342 #ifdef linux
343 TRACE("status=%02X !\n",
344 wcda->sc.cdsc_audiostatus);
345 #else
346 TRACE("status=%02X !\n",
347 wcda->sc.header.audio_status);
348 #endif
350 #ifdef linux
351 wcda->nCurTrack = wcda->sc.cdsc_trk;
352 wcda->dwCurFrame =
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;
356 #else
357 wcda->nCurTrack = wcda->sc.what.position.track_number;
358 wcda->dwCurFrame =
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;
362 #endif
363 #ifdef linux
364 TRACE("%02u-%02u:%02u:%02u \n",
365 wcda->sc.cdsc_trk,
366 wcda->sc.cdsc_absaddr.msf.minute,
367 wcda->sc.cdsc_absaddr.msf.second,
368 wcda->sc.cdsc_absaddr.msf.frame);
369 #else
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);
375 #endif
377 if (oldmode != wcda->cdaMode && oldmode == WINE_CDA_OPEN) {
378 if (!CDROM_Audio_GetTracksInfo(wcda)) {
379 WARN("error updating TracksInfo !\n");
380 return FALSE;
383 return TRUE;
384 #else
385 return FALSE;
386 #endif
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__)
395 #ifdef linux
396 struct cdrom_msf msf;
397 #else
398 struct ioc_play_msf msf;
399 #endif
401 #ifdef linux
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;
408 #else
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;
415 #endif
416 #ifdef linux
417 if (ioctl(wcda->unixdev, CDROMSTART))
418 #else
419 if (ioctl(wcda->unixdev, CDIOCSTART, NULL))
420 #endif
422 WARN("motor doesn't start !\n");
423 return -1;
425 #ifdef linux
426 if (ioctl(wcda->unixdev, CDROMPLAYMSF, &msf))
427 #else
428 if (ioctl(wcda->unixdev, CDIOCPLAYMSF, &msf))
429 #endif
431 WARN("device doesn't play !\n");
432 return -1;
434 #ifdef linux
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);
438 #else
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);
442 #endif
443 return 0;
444 #else
445 return -1;
446 #endif
449 /**************************************************************************
450 * CDROM_Audio_Stop [internal]
452 int CDROM_Audio_Stop(WINE_CDAUDIO* wcda)
454 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
455 int ret = 0;
456 #ifdef linux
457 ret = ioctl(wcda->unixdev, CDROMSTOP);
458 #else
459 ret = ioctl(wcda->unixdev, CDIOCSTOP, NULL);
460 #endif
461 return ret;
462 #else
463 return -1;
464 #endif
467 /**************************************************************************
468 * CDROM_Audio_Pause [internal]
470 int CDROM_Audio_Pause(WINE_CDAUDIO* wcda, int pauseOn)
472 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
473 int ret = 0;
474 #ifdef linux
475 ret = ioctl(wcda->unixdev, pauseOn ? CDROMPAUSE : CDROMRESUME);
476 #else
477 ret = ioctl(wcda->unixdev, pauseOn ? CDIOCPAUSE : CDIOCRESUME, NULL);
478 #endif
479 return ret;
480 #else
481 return -1;
482 #endif
485 /**************************************************************************
486 * CDROM_Audio_Seek [internal]
488 int CDROM_Audio_Seek(WINE_CDAUDIO* wcda, DWORD at)
490 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
491 int ret = 0;
492 #ifdef linux
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);
499 #else
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]);
506 #endif
507 return ret;
508 #else
509 return -1;
510 #endif
513 /**************************************************************************
514 * CDROM_SetDoor [internal]
516 int CDROM_SetDoor(WINE_CDAUDIO* wcda, int open)
518 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
519 int ret = 0;
520 #ifdef linux
521 if (open) {
522 ret = ioctl(wcda->unixdev, CDROMEJECT);
523 } else {
524 ret = ioctl(wcda->unixdev, CDROMEJECT, 1);
526 #else
527 ret = (ioctl(wcda->unixdev, CDIOCALLOW, NULL)) ||
528 (ioctl(wcda->unixdev, open ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
529 (ioctl(wcda->unixdev, CDIOCPREVENT, NULL));
530 #endif
531 wcda->nTracks = 0;
532 return ret;
533 #else
534 return -1;
535 #endif
538 /**************************************************************************
539 * CDROM_Reset [internal]
541 int CDROM_Reset(WINE_CDAUDIO* wcda)
543 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
544 int ret = 0;
545 #ifdef linux
546 ret = ioctl(wcda->unixdev, CDROMRESET);
547 #else
548 ret = ioctl(wcda->unixdev, CDIOCRESET, NULL);
549 #endif
550 return ret;
551 #else
552 return -1;
553 #endif
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)
566 break;
567 if (cur_vd_type > max_vd_type)
569 max_vd_type = cur_vd_type;
570 best_offs = offs;
573 return best_offs;
576 /**************************************************************************
577 * CDROM_Audio_GetSerial [internal]
579 DWORD CDROM_Audio_GetSerial(WINE_CDAUDIO* wcda)
581 unsigned long serial = 0;
582 int i;
583 DWORD dwFrame, msf;
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));
597 return serial;
600 /**************************************************************************
601 * CDROM_Data_GetSerial [internal]
603 DWORD CDROM_Data_GetSerial(WINE_CDAUDIO* wcda)
605 unsigned int offs = get_offs_best_voldesc(wcda->unixdev);
606 union {
607 unsigned long val;
608 unsigned char p[4];
609 } serial;
611 serial.val = 0;
612 if (offs)
614 BYTE buf[2048];
615 int i;
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];
628 return serial.val;
631 /**************************************************************************
632 * CDROM_GetSerial [internal]
634 DWORD CDROM_GetSerial(int drive)
636 WINE_CDAUDIO wcda;
637 DWORD serial = 0;
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);
646 LPSTR p;
648 if (media == CDS_AUDIO)
650 if (!(CDROM_Audio_GetCDStatus(&wcda))) {
651 ERR("couldn't get CD status !\n");
652 CDROM_Close(&wcda);
653 return 0;
655 serial = CDROM_Audio_GetSerial(&wcda);
657 else
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 " : "";
664 if (serial)
665 FIXME("%sCD serial number is %04x-%04x.\n",
666 p, HIWORD(serial), LOWORD(serial));
667 else
668 ERR("couldn't get %sCD serial !\n", p);
669 CDROM_Close(&wcda);
671 return serial;