2024-09-24 14:54:57 +02:00

243 lines
6.5 KiB
C

/* --------------------------------------------------------------------------
MusicBrainz -- The Internet music metadatabase
Copyright (C) 2013 Johannes Dewender
Copyright (C) 2006 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
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOCDBlockStorageDevice.h>
#include <IOKit/storage/IOCDMediaBSDClient.h>
#include "discid/discid.h"
#include "discid/discid_private.h"
#include "unix.h"
#define MB_DEFAULT_DEVICE "1" /* first disc drive (empty or not) */
#define TOC_BUFFER_LEN 2048
static int find_cd_block_devices(io_iterator_t *device_iterator)
{
mach_port_t master_port;
CFMutableDictionaryRef matching_dictionary;
if (IOMasterPort(MACH_PORT_NULL, &master_port) != KERN_SUCCESS)
return 0;
matching_dictionary = IOServiceMatching(kIOCDBlockStorageDeviceClass);
if (IOServiceGetMatchingServices(master_port, matching_dictionary,
device_iterator) != KERN_SUCCESS)
return 0;
else
return 1;
}
static int get_device_from_entry(io_registry_entry_t entry,
char *device_path, int max_len)
{
int return_value = 0;
size_t dev_path_len;;
CFStringRef cf_device_name;;
*device_path = '\0';
cf_device_name = IORegistryEntrySearchCFProperty(entry,
kIOServicePlane, CFSTR(kIOBSDNameKey),
kCFAllocatorDefault, kIORegistryIterateRecursively);
if (cf_device_name) {
strcpy(device_path, _PATH_DEV);
/* Add "r" before the device name to use the raw disk node
* without the buffering cache. */
strcat(device_path, "r");
dev_path_len = strlen(device_path);
if (CFStringGetCString(cf_device_name,
device_path + dev_path_len,
max_len - dev_path_len - 1,
kCFStringEncodingASCII))
return_value = 1;
CFRelease(cf_device_name);
}
return return_value;
}
static int get_device_from_number(int device_number,
char * buffer, int buffer_len) {
int return_value = 0;
int index = 0;
io_iterator_t device_iterator;
io_object_t device_object = IO_OBJECT_NULL;
if (!find_cd_block_devices(&device_iterator))
return 0;
while (index < device_number
&& (device_object = IOIteratorNext(device_iterator)))
index++;
if (index != device_number) {
return_value = 0;
} else {
return_value = get_device_from_entry(device_object,
buffer, buffer_len);
}
IOObjectRelease(device_object);
IOObjectRelease(device_iterator);
return return_value;
}
void mb_disc_unix_read_mcn(int fd, mb_disc_private *disc)
{
dk_cd_read_mcn_t cd_read_mcn;
bzero(&cd_read_mcn, sizeof(cd_read_mcn));
if(ioctl(fd, DKIOCCDREADMCN, &cd_read_mcn) == -1) {
fprintf(stderr, "Warning: Unable to read the disc's media catalog number.\n");
} else {
strncpy( disc->mcn, cd_read_mcn.mcn, MCN_STR_LENGTH );
}
}
void mb_disc_unix_read_isrc(int fd, mb_disc_private *disc, int track)
{
dk_cd_read_isrc_t cd_read_isrc;
bzero(&cd_read_isrc, sizeof(cd_read_isrc));
cd_read_isrc.track = track;
if(ioctl(fd, DKIOCCDREADISRC, &cd_read_isrc) == -1) {
fprintf(stderr, "Warning: Unable to read the international standard recording code (ISRC) for track %i\n", track);
return;
} else {
strncpy( disc->isrc[track], cd_read_isrc.isrc, ISRC_STR_LENGTH );
}
}
int mb_disc_has_feature_unportable(enum discid_feature feature) {
switch(feature) {
case DISCID_FEATURE_READ:
case DISCID_FEATURE_MCN:
case DISCID_FEATURE_ISRC:
return 1;
default:
return 0;
}
}
char *mb_disc_get_default_device_unportable(void)
{
return MB_DEFAULT_DEVICE;
}
int mb_disc_unix_read_toc_header(int fd, mb_disc_toc *mb_toc) {
dk_cd_read_toc_t toc;
CDTOC *cdToc;
mb_disc_toc_track *track;
int i, numDesc;
int track_num, min_track, max_track;
memset(&toc, 0, sizeof(toc));
toc.format = kCDTOCFormatTOC;
toc.formatAsTime = 0;
toc.buffer = (char *)malloc(TOC_BUFFER_LEN);
toc.bufferLength = TOC_BUFFER_LEN;
if (ioctl(fd, DKIOCCDREADTOC, &toc) < 0) {
return 0;
}
if (toc.bufferLength < sizeof(CDTOC)) {
return 0;
}
cdToc = (CDTOC *)toc.buffer;
numDesc = CDTOCGetDescriptorCount(cdToc);
min_track = -1;
max_track = -1;
for(i = 0; i < numDesc; i++) {
CDTOCDescriptor *desc = &cdToc->descriptors[i];
track = NULL;
/* A2 is the code for the lead-out position in the lead-in */
if (desc->point == 0xA2 && desc->adr == 1) {
track = &mb_toc->tracks[0];
}
/* actual track data, (adr 2-3 are for MCN and ISRC data) */
if (desc->point <= 99 && desc->adr == 1) {
track_num = desc->point;
track = &mb_toc->tracks[track_num];
if (min_track < 0 || min_track > track_num) {
min_track = track_num;
}
if (max_track < track_num) {
max_track = track_num;
}
}
if (track) {
track->address = CDConvertMSFToLBA(desc->p);
track->control = desc->control;
}
}
mb_toc->first_track_num = min_track;
mb_toc->last_track_num = max_track;
free(toc.buffer);
return 1;
}
int mb_disc_unix_read_toc_entry(int fd, int track_num, mb_disc_toc_track *toc) {
/* On Darwin the tracks are already filled along with the header */
return 1;
}
int mb_disc_read_unportable(mb_disc_private *disc, const char *device,
unsigned int features) {
int device_number;
char device_name[MAXPATHLEN] = "\0";
device_number = (int) strtol(device, NULL, 10);
if (device_number > 0) {
if (!get_device_from_number(device_number,
device_name, MAXPATHLEN)) {
snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
"no disc in drive number: %d", device_number);
return 0;
} else {
return mb_disc_unix_read(disc, device_name, features);
}
} else {
return mb_disc_unix_read(disc, device, features);
}
}