/* --------------------------------------------------------------------------
MusicBrainz -- The Internet music metadatabase
Copyright (C) 2008 Patrick Hurrelmann
Copyright (C) 2006 Matthias Friedrich
Copyright (C) 2000 Robert Kaye
Copyright (C) 1999 Marc E E van Woerkom
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
$Id$
--------------------------------------------------------------------------- */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>
#include <arpa/inet.h>
#include "discid/discid.h"
#include "discid/discid_private.h"
#define CD_FRAMES 75 /* per second */
#define CD_DATA_TRACK 0x04
#define CD_LEADOUT 0xAA
#define MB_DEFAULT_DEVICE "/dev/acd0"
#define XA_INTERVAL ((60 + 90 + 2) * CD_FRAMES)
static int read_toc_header(int fd, int *first, int *last) {
struct ioc_toc_header th;
struct ioc_read_toc_single_entry te;
int ret = ioctl(fd, CDIOREADTOCHEADER, &th);
if ( ret < 0 )
return ret; /* error */
*first = th.starting_track;
*last = th.ending_track;
/*
* Hide the last track if this is a multisession disc. Note that
* currently only dual-session discs with one track in the second
* session are handled correctly.
*/
te.address_format = CD_LBA_FORMAT;
te.track = th.ending_track;
ret = ioctl(fd, CDIOREADTOCENTRY, &te);
if (( te.entry.control & CD_DATA_TRACK) != 0 )
(*last)--;
return ret;
}
static int read_toc_entry(int fd, int track_num, unsigned long *lba) {
struct ioc_read_toc_single_entry te;
int ret;
te.track = track_num;
te.address_format = CD_LBA_FORMAT;
ret = ioctl(fd, CDIOREADTOCENTRY, &te);
assert( te.address_format == CD_LBA_FORMAT );
if ( ret == 0 )
*lba = ntohl(te.entry.addr.lba);
return ret;
}
static int read_leadout(int fd, unsigned long *lba) {
struct ioc_toc_header th;
struct ioc_read_toc_single_entry te;
int ret;
ret = ioctl(fd, CDIOREADTOCHEADER, &th);
te.track = th.ending_track;
te.address_format = CD_LBA_FORMAT;
ret = ioctl(fd, CDIOREADTOCENTRY, &te);
if (( te.entry.control & CD_DATA_TRACK) != 0 ) {
*lba = ntohl(te.entry.addr.lba) - 11400;
return ret;
}
return read_toc_entry(fd, CD_LEADOUT, lba);
}
char *mb_disc_get_default_device_unportable(void) {
return MB_DEFAULT_DEVICE;
}
int mb_disc_has_feature_unportable(enum discid_feature feature) {
switch(feature) {
case DISCID_FEATURE_READ:
return 1;
default:
return 0;
}
}
int mb_disc_read_unportable(mb_disc_private *disc, const char *device) {
int fd;
unsigned long lba;
int first, last;
int i;
if ( (fd = open(device, O_RDONLY | O_NONBLOCK)) < 0 ) {
snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
"cannot open device `%s'", device);
return 0;
}
/*
* Find the numbers of the first track (usually 1) and the last track.
*/
if ( read_toc_header(fd, &first, &last) < 0 ) {
snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
"cannot read table of contents");
close(fd);
return 0;
}
/* basic error checking */
if ( last == 0 ) {
snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
"this disc has no tracks");
close(fd);
return 0;
}
disc->first_track_num = first;
disc->last_track_num = last;
/*
* Get the logical block address (lba) for the end of the audio data.
* The "LEADOUT" track is the track beyond the final audio track, so
* we're looking for the block address of the LEADOUT track.
*/
read_leadout(fd, &lba);
disc->track_offsets[0] = lba + 150;
/*
* Now, for every track, find out the block address where it starts.
*/
for (i = first; i <= last; i++) {
read_toc_entry(fd, i, &lba);
disc->track_offsets[i] = lba + 150;
}
close(fd);
return 1;
}
/* EOF */