summaryrefslogtreecommitdiff
path: root/contrib/libcdio/_cdio_osx.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libcdio/_cdio_osx.c')
-rw-r--r--contrib/libcdio/_cdio_osx.c1470
1 files changed, 1470 insertions, 0 deletions
diff --git a/contrib/libcdio/_cdio_osx.c b/contrib/libcdio/_cdio_osx.c
new file mode 100644
index 000000000..f754933c6
--- /dev/null
+++ b/contrib/libcdio/_cdio_osx.c
@@ -0,0 +1,1470 @@
+/*
+ $Id: _cdio_osx.c,v 1.4 2005/01/01 02:43:57 rockyb Exp $
+
+ Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com>
+ from vcdimager code:
+ Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
+ and VideoLAN code Copyright (C) 1998-2001 VideoLAN
+ Authors: Johan Bilien <jobi@via.ecp.fr>
+ Gildas Bazin <gbazin@netcourrier.com>
+ Jon Lech Johansen <jon-vl@nanocrew.net>
+ Derk-Jan Hartman <hartman at videolan.org>
+ Justin F. Hallett <thesin@southofheaven.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* This file contains OSX-specific code and implements low-level
+ control of the CD drive.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+static const char _rcsid[] = "$Id: _cdio_osx.c,v 1.4 2005/01/01 02:43:57 rockyb Exp $";
+
+#include <cdio/logging.h>
+#include <cdio/sector.h>
+#include <cdio/util.h>
+#include "cdio_assert.h"
+#include "cdio_private.h"
+
+#include <string.h>
+
+#ifdef HAVE_DARWIN_CDROM
+#undef VERSION
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+
+#include <mach/mach.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/scsi-commands/SCSITaskLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <mach/mach_error.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+
+#include <paths.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h>
+#include <IOKit/storage/IOCDTypes.h>
+#include <IOKit/storage/IODVDTypes.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/storage/IODVDMedia.h>
+#include <IOKit/storage/IOCDMediaBSDClient.h>
+#include <IOKit/storage/IODVDMediaBSDClient.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+
+#define kIOCDBlockStorageDeviceClassString "IOCDBlockStorageDevice"
+
+/* Note leadout is normally defined 0xAA, But on OSX 0xA0 is "lead in" while
+ 0xA2 is "lead out". Don't ask me why. */
+#define OSX_CDROM_LEADOUT_TRACK 0xA2
+
+#define TOTAL_TRACKS (p_env->i_last_track - p_env->gen.i_first_track + 1)
+
+#define CDROM_CDI_TRACK 0x1
+#define CDROM_XA_TRACK 0x2
+
+typedef enum {
+ _AM_NONE,
+ _AM_OSX,
+} access_mode_t;
+
+#define MAX_SERVICE_NAME 1000
+typedef struct {
+ /* Things common to all drivers like this.
+ This must be first. */
+ generic_img_private_t gen;
+
+ access_mode_t access_mode;
+
+ /* Track information */
+ CDTOC *pTOC;
+ int i_descriptors;
+ track_t i_last_track; /* highest track number */
+ track_t i_last_session; /* highest session number */
+ track_t i_first_session; /* first session number */
+ lsn_t *pp_lba;
+ io_service_t MediaClass_service;
+ char psz_MediaClass_service[MAX_SERVICE_NAME];
+ SCSITaskDeviceInterface **pp_scsiTaskDeviceInterface;
+
+} _img_private_t;
+
+static bool read_toc_osx (void *p_user_data);
+
+/****
+ * GetRegistryEntryProperties - Gets the registry entry properties for
+ * an io_service_t.
+ *****/
+
+static CFMutableDictionaryRef
+GetRegistryEntryProperties ( io_service_t service )
+{
+ IOReturn err = kIOReturnSuccess;
+ CFMutableDictionaryRef dict = 0;
+
+ err = IORegistryEntryCreateCFProperties (service, &dict, kCFAllocatorDefault, 0);
+ if ( err != kIOReturnSuccess )
+ cdio_warn( "IORegistryEntryCreateCFProperties: 0x%08x", err );
+
+ return dict;
+}
+
+
+static bool
+init_osx(_img_private_t *p_env) {
+ mach_port_t port;
+ char *psz_devname;
+ kern_return_t ret;
+ io_iterator_t iterator;
+
+ p_env->gen.fd = open( p_env->gen.source_name, O_RDONLY | O_NONBLOCK );
+ if (-1 == p_env->gen.fd) {
+ cdio_warn("Failed to open %s: %s", p_env->gen.source_name,
+ strerror(errno));
+ return false;
+ }
+
+ /* get the device name */
+ psz_devname = strrchr( p_env->gen.source_name, '/');
+ if( NULL != psz_devname )
+ ++psz_devname;
+ else
+ psz_devname = p_env->gen.source_name;
+
+ /* unraw the device name */
+ if( *psz_devname == 'r' )
+ ++psz_devname;
+
+ /* get port for IOKit communication */
+ ret = IOMasterPort( MACH_PORT_NULL, &port );
+
+ if( ret != KERN_SUCCESS )
+ {
+ cdio_warn( "IOMasterPort: 0x%08x", ret );
+ return false;
+ }
+
+ ret = IOServiceGetMatchingServices( port,
+ IOBSDNameMatching(port, 0, psz_devname),
+ &iterator );
+
+ /* get service iterator for the device */
+ if( ret != KERN_SUCCESS )
+ {
+ cdio_warn( "IOServiceGetMatchingServices: 0x%08x", ret );
+ return false;
+ }
+
+ /* first service */
+ p_env->MediaClass_service = IOIteratorNext( iterator );
+ IOObjectRelease( iterator );
+
+ /* search for kIOCDMediaClass or kIOCDVDMediaClass */
+ while( p_env->MediaClass_service &&
+ (!IOObjectConformsTo(p_env->MediaClass_service, kIOCDMediaClass)) &&
+ (!IOObjectConformsTo(p_env->MediaClass_service, kIODVDMediaClass)) )
+ {
+
+ ret = IORegistryEntryGetParentIterator( p_env->MediaClass_service,
+ kIOServicePlane,
+ &iterator );
+ if( ret != KERN_SUCCESS )
+ {
+ cdio_warn( "IORegistryEntryGetParentIterator: 0x%08x", ret );
+ IOObjectRelease( p_env->MediaClass_service );
+ return false;
+ }
+
+ IOObjectRelease( p_env->MediaClass_service );
+ p_env->MediaClass_service = IOIteratorNext( iterator );
+ IOObjectRelease( iterator );
+ }
+
+ if ( 0 == p_env->MediaClass_service ) {
+ cdio_warn( "search for kIOCDMediaClass/kIODVDMediaClass came up empty" );
+ return false;
+ }
+
+ /* Save the name so we can compare against this in case we have to do
+ another scan. FIXME: this is hoaky and there's got to be a better
+ variable to test or way to do.
+ */
+ IORegistryEntryGetPath(p_env->MediaClass_service, kIOServicePlane,
+ p_env->psz_MediaClass_service);
+ return true;
+}
+
+/*!
+ Run a SCSI MMC command.
+
+ cdio CD structure set by cdio_open().
+ i_timeout time in milliseconds we will wait for the command
+ to complete. If this value is -1, use the default
+ time-out value.
+ p_buf Buffer for data, both sending and receiving
+ i_buf Size of buffer
+ e_direction direction the transfer is to go.
+ cdb CDB bytes. All values that are needed should be set on
+ input. We'll figure out what the right CDB length should be.
+
+ We return true if command completed successfully and false if not.
+ */
+static int
+run_scsi_cmd_osx( const void *p_user_data,
+ unsigned int i_timeout_ms,
+ unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb,
+ scsi_mmc_direction_t e_direction,
+ unsigned int i_buf, /*in/out*/ void *p_buf )
+{
+
+#ifndef SCSI_MMC_FIXED
+ return 2;
+#else
+ const _img_private_t *p_env = p_user_data;
+ SCSITaskDeviceInterface **sc;
+ SCSITaskInterface **cmd = NULL;
+ IOVirtualRange iov;
+ SCSI_Sense_Data senseData;
+ SCSITaskStatus status;
+ UInt64 bytesTransferred;
+ IOReturn ioReturnValue;
+ int ret = 0;
+
+ if (NULL == p_user_data) return 2;
+
+ /* Make sure pp_scsiTaskDeviceInterface is initialized. FIXME: The code
+ should probably be reorganized better for this. */
+ if (!p_env->gen.toc_init) read_toc_osx (p_user_data) ;
+
+ sc = p_env->pp_scsiTaskDeviceInterface;
+
+ if (NULL == sc) return 3;
+
+ cmd = (*sc)->CreateSCSITask(sc);
+ if (cmd == NULL) {
+ cdio_warn("Failed to create SCSI task");
+ return -1;
+ }
+
+ iov.address = (IOVirtualAddress) p_buf;
+ iov.length = i_buf;
+
+ ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd, (UInt8 *) p_cdb,
+ i_cdb);
+ if (ioReturnValue != kIOReturnSuccess) {
+ cdio_warn("SetCommandDescriptorBlock failed with status %x",
+ ioReturnValue);
+ return -1;
+ }
+
+ ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, i_buf,
+ (SCSI_MMC_DATA_READ == e_direction ) ?
+ kSCSIDataTransfer_FromTargetToInitiator :
+ kSCSIDataTransfer_FromInitiatorToTarget);
+ if (ioReturnValue != kIOReturnSuccess) {
+ cdio_warn("SetScatterGatherEntries failed with status %x", ioReturnValue);
+ return -1;
+ }
+
+ ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, i_timeout_ms );
+ if (ioReturnValue != kIOReturnSuccess) {
+ cdio_warn("SetTimeoutDuration failed with status %x", ioReturnValue);
+ return -1;
+ }
+
+ memset(&senseData, 0, sizeof(senseData));
+
+ ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,&senseData, &status, &
+ bytesTransferred);
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ cdio_warn("Command execution failed with status %x", ioReturnValue);
+ return -1;
+ }
+
+ if (cmd != NULL) {
+ (*cmd)->Release(cmd);
+ }
+
+ return (ret);
+#endif
+}
+
+/***************************************************************************
+ * GetDeviceIterator - Gets an io_iterator_t for our class type
+ ***************************************************************************/
+
+static io_iterator_t
+GetDeviceIterator ( const char * deviceClass )
+{
+
+ IOReturn err = kIOReturnSuccess;
+ io_iterator_t iterator = MACH_PORT_NULL;
+
+ err = IOServiceGetMatchingServices ( kIOMasterPortDefault,
+ IOServiceMatching ( deviceClass ),
+ &iterator );
+ check ( err == kIOReturnSuccess );
+
+ return iterator;
+
+}
+
+/***************************************************************************
+ * GetFeaturesFlagsForDrive -Gets the bitfield which represents the
+ * features flags.
+ ***************************************************************************/
+
+static bool
+GetFeaturesFlagsForDrive ( CFDictionaryRef dict,
+ uint32_t *i_cdFlags,
+ uint32_t *i_dvdFlags )
+{
+ CFDictionaryRef propertiesDict = 0;
+ CFNumberRef flagsNumberRef = 0;
+
+ *i_cdFlags = 0;
+ *i_dvdFlags= 0;
+
+ propertiesDict = ( CFDictionaryRef )
+ CFDictionaryGetValue ( dict,
+ CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
+
+ if ( propertiesDict == 0 ) return false;
+
+ /* Get the CD features */
+ flagsNumberRef = ( CFNumberRef )
+ CFDictionaryGetValue ( propertiesDict,
+ CFSTR ( kIOPropertySupportedCDFeatures ) );
+ if ( flagsNumberRef != 0 ) {
+ CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_cdFlags );
+ }
+
+ /* Get the DVD features */
+ flagsNumberRef = ( CFNumberRef )
+ CFDictionaryGetValue ( propertiesDict,
+ CFSTR ( kIOPropertySupportedDVDFeatures ) );
+ if ( flagsNumberRef != 0 ) {
+ CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_dvdFlags );
+ }
+
+ return true;
+}
+
+/*!
+ Get disc type associated with the cd object.
+*/
+static discmode_t
+get_discmode_osx (void *p_user_data)
+{
+ _img_private_t *p_env = p_user_data;
+ char str[10];
+ int32_t i_discmode = CDIO_DISC_MODE_ERROR;
+ CFDictionaryRef propertiesDict = 0;
+ CFStringRef data;
+
+ propertiesDict = GetRegistryEntryProperties ( p_env->MediaClass_service );
+
+ if ( propertiesDict == 0 ) return i_discmode;
+
+ data = ( CFStringRef )
+ CFDictionaryGetValue ( propertiesDict, CFSTR ( kIODVDMediaTypeKey ) );
+
+ if( CFStringGetCString( data, str, sizeof(str),
+ kCFStringEncodingASCII ) ) {
+ if (0 == strncmp(str, "DVD+R", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_PR;
+ else if (0 == strncmp(str, "DVD+RW", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_PRW;
+ else if (0 == strncmp(str, "DVD-R", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_R;
+ else if (0 == strncmp(str, "DVD-RW", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_RW;
+ else if (0 == strncmp(str, "DVD-ROM", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_ROM;
+ else if (0 == strncmp(str, "DVD-RAM", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_DVD_RAM;
+ else if (0 == strncmp(str, "CD-ROM", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_CD_DATA;
+ else if (0 == strncmp(str, "CDR", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_CD_DATA;
+ else if (0 == strncmp(str, "CDRW", strlen(str)) )
+ i_discmode = CDIO_DISC_MODE_CD_DATA;
+ //?? Handled by below? CFRelease( data );
+ }
+ CFRelease( propertiesDict );
+ if (CDIO_DISC_MODE_CD_DATA == i_discmode) {
+ /* Need to do more classification */
+ return get_discmode_cd_generic(p_user_data);
+ }
+ return i_discmode;
+
+}
+
+static io_service_t
+get_drive_service_osx(const _img_private_t *p_env)
+{
+ io_service_t service;
+ io_iterator_t service_iterator;
+
+ service_iterator = GetDeviceIterator ( kIOCDBlockStorageDeviceClassString );
+
+ if( service_iterator == MACH_PORT_NULL ) return 0;
+
+ service = IOIteratorNext( service_iterator );
+ if( service == 0 ) return 0;
+
+ do
+ {
+ char psz_service[MAX_SERVICE_NAME];
+ IORegistryEntryGetPath(service, kIOServicePlane, psz_service);
+ psz_service[MAX_SERVICE_NAME-1] = '\0';
+
+ /* FIXME: This is all hoaky. Here we need info from a parent class,
+ psz_service of what we opened above. We are relying on the
+ fact that the name will be a substring of the name we
+ openned with.
+ */
+ if (0 == strncmp(psz_service, p_env->psz_MediaClass_service,
+ strlen(psz_service))) {
+ /* Found our device */
+ IOObjectRelease( service_iterator );
+ return service;
+ }
+
+ IOObjectRelease( service );
+
+ } while( ( service = IOIteratorNext( service_iterator ) ) != 0 );
+
+ IOObjectRelease( service_iterator );
+ return service;
+}
+
+static void
+get_drive_cap_osx(const void *p_user_data,
+ /*out*/ cdio_drive_read_cap_t *p_read_cap,
+ /*out*/ cdio_drive_write_cap_t *p_write_cap,
+ /*out*/ cdio_drive_misc_cap_t *p_misc_cap)
+{
+ const _img_private_t *p_env = p_user_data;
+ uint32_t i_cdFlags;
+ uint32_t i_dvdFlags;
+
+ io_service_t service = get_drive_service_osx(p_env);
+
+ if( service == 0 ) goto err_exit;
+
+ /* Found our device */
+ {
+ CFDictionaryRef properties = GetRegistryEntryProperties ( service );
+
+ if (! GetFeaturesFlagsForDrive ( properties, &i_cdFlags,
+ &i_dvdFlags ) ) {
+ IOObjectRelease( service );
+ goto err_exit;
+ }
+
+ /* Reader */
+
+ if ( 0 != (i_cdFlags & kCDFeaturesAnalogAudioMask) )
+ *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO;
+
+ if ( 0 != (i_cdFlags & kCDFeaturesWriteOnceMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R;
+
+ if ( 0 != (i_cdFlags & kCDFeaturesCDDAStreamAccurateMask) )
+ *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA;
+
+ if ( 0 != (i_dvdFlags & kDVDFeaturesReadStructuresMask) )
+ *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM;
+
+ if ( 0 != (i_cdFlags & kCDFeaturesReWriteableMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW;
+
+ if ( 0 != (i_dvdFlags & kDVDFeaturesWriteOnceMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R;
+
+ if ( 0 != (i_dvdFlags & kDVDFeaturesRandomWriteableMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM;
+
+ if ( 0 != (i_dvdFlags & kDVDFeaturesReWriteableMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RW;
+
+ /***
+ if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRMask) )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PR;
+
+ if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRWMask )
+ *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PRW;
+ ***/
+
+ /* FIXME: fill out. For now assume CD-ROM is relatively modern. */
+ *p_misc_cap = (
+ CDIO_DRIVE_CAP_MISC_CLOSE_TRAY
+ | CDIO_DRIVE_CAP_MISC_EJECT
+ | CDIO_DRIVE_CAP_MISC_LOCK
+ | CDIO_DRIVE_CAP_MISC_SELECT_SPEED
+ | CDIO_DRIVE_CAP_MISC_MULTI_SESSION
+ | CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED
+ | CDIO_DRIVE_CAP_MISC_RESET
+ | CDIO_DRIVE_CAP_MCN
+ | CDIO_DRIVE_CAP_ISRC
+ );
+
+ IOObjectRelease( service );
+ }
+
+ return;
+
+ err_exit:
+ *p_misc_cap = *p_write_cap = *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN;
+ return;
+}
+
+#if 1
+/****************************************************************************
+ * GetDriveDescription - Gets drive description.
+ ****************************************************************************/
+
+static bool
+get_hwinfo_osx ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
+{
+ _img_private_t *p_env = (_img_private_t *) p_cdio->env;
+ io_service_t service = get_drive_service_osx(p_env);
+
+ if ( service == 0 ) return false;
+
+ /* Found our device */
+ {
+ CFStringRef vendor = NULL;
+ CFStringRef product = NULL;
+ CFStringRef revision = NULL;
+
+ CFDictionaryRef properties = GetRegistryEntryProperties ( service );
+ CFDictionaryRef deviceDict = ( CFDictionaryRef )
+ CFDictionaryGetValue ( properties,
+ CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
+
+ if ( deviceDict == 0 ) return false;
+
+ vendor = ( CFStringRef )
+ CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) );
+
+ if ( CFStringGetCString( vendor,
+ (char *) &(hw_info->psz_vendor),
+ sizeof(hw_info->psz_vendor),
+ kCFStringEncodingASCII ) )
+ CFRelease( vendor );
+
+ product = ( CFStringRef )
+ CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) );
+
+ if ( CFStringGetCString( product,
+ (char *) &(hw_info->psz_model),
+ sizeof(hw_info->psz_model),
+ kCFStringEncodingASCII ) )
+ CFRelease( product );
+
+ revision = ( CFStringRef )
+ CFDictionaryGetValue ( deviceDict,
+ CFSTR ( kIOPropertyProductRevisionLevelKey ) );
+
+ if ( CFStringGetCString( product,
+ (char *) &(hw_info->psz_revision),
+ sizeof(hw_info->psz_revision),
+ kCFStringEncodingASCII ) )
+ CFRelease( revision );
+ }
+ return true;
+
+}
+#endif
+
+/*!
+ Return the media catalog number MCN.
+
+ Note: string is malloc'd so caller should free() then returned
+ string when done with it.
+
+ */
+static const cdtext_t *
+get_cdtext_osx (void *p_user_data, track_t i_track)
+{
+ return NULL;
+}
+
+static void
+_free_osx (void *p_user_data) {
+ _img_private_t *p_env = p_user_data;
+ if (NULL == p_env) return;
+ cdio_generic_free(p_env);
+ if (NULL != p_env->pp_lba) free((void *) p_env->pp_lba);
+ if (NULL != p_env->pTOC) free((void *) p_env->pTOC);
+ IOObjectRelease( p_env->MediaClass_service );
+
+ if (NULL != p_env->pp_scsiTaskDeviceInterface)
+ ( *(p_env->pp_scsiTaskDeviceInterface) )->
+ Release ( (p_env->pp_scsiTaskDeviceInterface) );
+
+}
+
+/*!
+ Reads nblocks of mode2 form2 sectors from cd device into data starting
+ from lsn.
+ Returns 0 if no error.
+ */
+static int
+_get_read_mode1_sectors_osx (void *user_data, void *data, lsn_t lsn,
+ bool b_form2, unsigned int nblocks)
+{
+ _img_private_t *env = user_data;
+ dk_cd_read_t cd_read;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ cd_read.sectorArea = kCDSectorAreaUser;
+ cd_read.buffer = data;
+ cd_read.sectorType = kCDSectorTypeMode1;
+
+ if (b_form2) {
+ cd_read.offset = lsn * kCDSectorSizeMode2;
+ cd_read.bufferLength = kCDSectorSizeMode2 * nblocks;
+ } else {
+ cd_read.offset = lsn * kCDSectorSizeMode1;
+ cd_read.bufferLength = kCDSectorSizeMode1 * nblocks;
+ }
+
+ if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
+ {
+ cdio_info( "could not read block %d, %s", lsn, strerror(errno) );
+ return -1;
+ }
+ return 0;
+}
+
+
+/*!
+ Reads nblocks of mode2 form2 sectors from cd device into data starting
+ from lsn.
+ Returns 0 if no error.
+ */
+static int
+_get_read_mode2_sectors_osx (void *user_data, void *data, lsn_t lsn,
+ bool b_form2, unsigned int nblocks)
+{
+ _img_private_t *env = user_data;
+ dk_cd_read_t cd_read;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ cd_read.sectorArea = kCDSectorAreaUser;
+ cd_read.buffer = data;
+
+ if (b_form2) {
+ cd_read.offset = lsn * kCDSectorSizeMode2Form2;
+ cd_read.sectorType = kCDSectorTypeMode2Form2;
+ cd_read.bufferLength = kCDSectorSizeMode2Form2 * nblocks;
+ } else {
+ cd_read.offset = lsn * kCDSectorSizeMode2Form1;
+ cd_read.sectorType = kCDSectorTypeMode2Form1;
+ cd_read.bufferLength = kCDSectorSizeMode2Form1 * nblocks;
+ }
+
+ if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
+ {
+ cdio_info( "could not read block %d, %s", lsn, strerror(errno) );
+ return -1;
+ }
+ return 0;
+}
+
+
+/*!
+ Reads a single audio sector from CD device into data starting from lsn.
+ Returns 0 if no error.
+ */
+static int
+_get_read_audio_sectors_osx (void *user_data, void *data, lsn_t lsn,
+ unsigned int nblocks)
+{
+ _img_private_t *env = user_data;
+ dk_cd_read_t cd_read;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ cd_read.offset = lsn * kCDSectorSizeCDDA;
+ cd_read.sectorArea = kCDSectorAreaUser;
+ cd_read.sectorType = kCDSectorTypeCDDA;
+
+ cd_read.buffer = data;
+ cd_read.bufferLength = kCDSectorSizeCDDA * nblocks;
+
+ if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
+ {
+ cdio_info( "could not read block %d", lsn );
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ Reads a single mode2 sector from cd device into data starting
+ from lsn. Returns 0 if no error.
+ */
+static int
+_get_read_mode1_sector_osx (void *user_data, void *data, lsn_t lsn,
+ bool b_form2)
+{
+ return _get_read_mode1_sectors_osx(user_data, data, lsn, b_form2, 1);
+}
+
+/*!
+ Reads a single mode2 sector from cd device into data starting
+ from lsn. Returns 0 if no error.
+ */
+static int
+_get_read_mode2_sector_osx (void *user_data, void *data, lsn_t lsn,
+ bool b_form2)
+{
+ return _get_read_mode2_sectors_osx(user_data, data, lsn, b_form2, 1);
+}
+
+/*!
+ Set the key "arg" to "value" in source device.
+*/
+static int
+_set_arg_osx (void *user_data, const char key[], const char value[])
+{
+ _img_private_t *env = user_data;
+
+ if (!strcmp (key, "source"))
+ {
+ if (!value)
+ return -2;
+
+ free (env->gen.source_name);
+
+ env->gen.source_name = strdup (value);
+ }
+ else if (!strcmp (key, "access-mode"))
+ {
+ if (!strcmp(value, "OSX"))
+ env->access_mode = _AM_OSX;
+ else
+ cdio_warn ("unknown access type: %s. ignored.", value);
+ }
+ else
+ return -1;
+
+ return 0;
+}
+
+#if 0
+static void TestDevice(_img_private_t *p_env, io_service_t service)
+{
+ SInt32 score;
+ HRESULT herr;
+ kern_return_t err;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ MMCDeviceInterface **mmcInterface = NULL;
+
+ /* Create the IOCFPlugIn interface so we can query it. */
+
+ err = IOCreatePlugInInterfaceForService ( service,
+ kIOMMCDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score );
+ if ( err != noErr ) {
+ printf("IOCreatePlugInInterfaceForService returned %d\n", err);
+ return;
+ }
+
+ /* Query the interface for the MMCDeviceInterface. */
+
+ herr = ( *plugInInterface )->QueryInterface ( plugInInterface,
+ CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ),
+ ( LPVOID ) &mmcInterface );
+
+ if ( herr != S_OK ) {
+ printf("QueryInterface returned %ld\n", herr);
+ return;
+ }
+
+ p_env->pp_scsiTaskDeviceInterface =
+ ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface );
+
+ if ( NULL == p_env->pp_scsiTaskDeviceInterface ) {
+ printf("GetSCSITaskDeviceInterface returned NULL\n");
+ return;
+ }
+
+ ( *mmcInterface )->Release ( mmcInterface );
+ IODestroyPlugInInterface ( plugInInterface );
+}
+#endif
+
+/*!
+ Read and cache the CD's Track Table of Contents and track info.
+ Return false if successful or true if an error.
+*/
+static bool
+read_toc_osx (void *p_user_data)
+{
+ _img_private_t *p_env = p_user_data;
+ CFDictionaryRef propertiesDict = 0;
+ CFDataRef data;
+
+ /* create a CF dictionary containing the TOC */
+ propertiesDict = GetRegistryEntryProperties( p_env->MediaClass_service );
+
+ if ( 0 == propertiesDict ) {
+ return false;
+ }
+
+ /* get the TOC from the dictionary */
+ data = (CFDataRef) CFDictionaryGetValue( propertiesDict,
+ CFSTR(kIOCDMediaTOCKey) );
+ if ( data != NULL ) {
+ CFRange range;
+ CFIndex buf_len;
+
+ buf_len = CFDataGetLength( data ) + 1;
+ range = CFRangeMake( 0, buf_len );
+
+ if( ( p_env->pTOC = (CDTOC *)malloc( buf_len ) ) != NULL ) {
+ CFDataGetBytes( data, range, (u_char *) p_env->pTOC );
+ } else {
+ cdio_warn( "Trouble allocating CDROM TOC" );
+ return false;
+ }
+ } else {
+ cdio_warn( "Trouble reading TOC" );
+ return false;
+ }
+
+ /* TestDevice(p_env, service); */
+ CFRelease( propertiesDict );
+
+ p_env->i_descriptors = CDTOCGetDescriptorCount ( p_env->pTOC );
+
+ /* Read in starting sectors. There may be non-tracks mixed in with
+ the real tracks. So find the first and last track number by
+ scanning. Also find the lead-out track position.
+ */
+ {
+ int i, i_leadout = -1;
+
+ CDTOCDescriptor *pTrackDescriptors;
+
+ p_env->pp_lba = malloc( p_env->i_descriptors * sizeof(int) );
+ if( p_env->pp_lba == NULL )
+ {
+ cdio_warn("Out of memory in allocating track starting LSNs" );
+ free( p_env->pTOC );
+ return false;
+ }
+
+ pTrackDescriptors = p_env->pTOC->descriptors;
+
+ p_env->gen.i_first_track = CDIO_CD_MAX_TRACKS+1;
+ p_env->i_last_track = CDIO_CD_MIN_TRACK_NO;
+ p_env->i_first_session = CDIO_CD_MAX_TRACKS+1;
+ p_env->i_last_session = CDIO_CD_MIN_TRACK_NO;
+
+ for( i = 0; i <= p_env->i_descriptors; i++ )
+ {
+ track_t i_track = pTrackDescriptors[i].point;
+ session_t i_session = pTrackDescriptors[i].session;
+
+ cdio_debug( "point: %d, tno: %d, session: %d, adr: %d, control:%d, "
+ "address: %d:%d:%d, p: %d:%d:%d",
+ i_track,
+ pTrackDescriptors[i].tno, i_session,
+ pTrackDescriptors[i].adr, pTrackDescriptors[i].control,
+ pTrackDescriptors[i].address.minute,
+ pTrackDescriptors[i].address.second,
+ pTrackDescriptors[i].address.frame,
+ pTrackDescriptors[i].p.minute,
+ pTrackDescriptors[i].p.second,
+ pTrackDescriptors[i].p.frame );
+
+ /* track information has adr = 1 */
+ if ( 0x01 != pTrackDescriptors[i].adr )
+ continue;
+
+ if( i_track == OSX_CDROM_LEADOUT_TRACK )
+ i_leadout = i;
+
+ if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
+ continue;
+
+ if (p_env->gen.i_first_track > i_track)
+ p_env->gen.i_first_track = i_track;
+
+ if (p_env->i_last_track < i_track)
+ p_env->i_last_track = i_track;
+
+ if (p_env->i_first_session > i_session)
+ p_env->i_first_session = i_session;
+
+ if (p_env->i_last_session < i_session)
+ p_env->i_last_session = i_session;
+ }
+
+ /* Now that we know what the first track number is, we can make sure
+ index positions are ordered starting at 0.
+ */
+ for( i = 0; i <= p_env->i_descriptors; i++ )
+ {
+ track_t i_track = pTrackDescriptors[i].point;
+
+ if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
+ continue;
+
+ /* Note what OSX calls a LBA we call an LSN. So below re we
+ really have have MSF -> LSN -> LBA.
+ */
+ p_env->pp_lba[i_track - p_env->gen.i_first_track] =
+ cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i].p ));
+ }
+
+ if( i_leadout == -1 )
+ {
+ cdio_warn( "CD leadout not found" );
+ free( p_env->pp_lba );
+ free( (void *) p_env->pTOC );
+ return false;
+ }
+
+ /* Set leadout sector.
+ Note what OSX calls a LBA we call an LSN. So below re we
+ really have have MSF -> LSN -> LBA.
+ */
+ p_env->pp_lba[TOTAL_TRACKS] =
+ cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p ));
+ p_env->gen.i_tracks = TOTAL_TRACKS;
+ }
+
+ p_env->gen.toc_init = true;
+
+ return( true );
+
+}
+
+/*!
+ Return the starting LSN track number
+ i_track in obj. Track numbers start at 1.
+ The "leadout" track is specified either by
+ using i_track LEADOUT_TRACK or the total tracks+1.
+ False is returned if there is no track entry.
+*/
+static lsn_t
+get_track_lba_osx(void *p_user_data, track_t i_track)
+{
+ _img_private_t *p_env = p_user_data;
+
+ if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
+ if (!p_env->gen.toc_init) return CDIO_INVALID_LSN;
+
+ if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->i_last_track+1;
+
+ if (i_track > p_env->i_last_track + 1 || i_track < p_env->gen.i_first_track) {
+ return CDIO_INVALID_LSN;
+ } else {
+ return p_env->pp_lba[i_track - p_env->gen.i_first_track];
+ }
+}
+
+/*!
+ Eject media . Return 1 if successful, 0 otherwise.
+
+ The only way to cleanly unmount the disc under MacOS X is to use the
+ 'disktool' command line utility. It uses the non-public Disk
+ Arbitration API, which can not be used by Cocoa or Carbon
+ applications.
+
+ */
+
+static int
+_eject_media_osx (void *user_data) {
+
+ _img_private_t *p_env = user_data;
+
+ FILE *p_eject;
+ char *psz_disk;
+ char sz_cmd[32];
+
+ if( ( psz_disk = (char *)strstr( p_env->gen.source_name, "disk" ) ) != NULL &&
+ strlen( psz_disk ) > 4 )
+ {
+#define EJECT_CMD "/usr/sbin/hdiutil eject %s"
+ snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_disk );
+#undef EJECT_CMD
+
+ if( ( p_eject = popen( sz_cmd, "r" ) ) != NULL )
+ {
+ char psz_result[0x200];
+ int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_eject );
+
+ if( i_ret == 0 && ferror( p_eject ) != 0 )
+ {
+ pclose( p_eject );
+ return 0;
+ }
+
+ pclose( p_eject );
+
+ psz_result[ i_ret ] = 0;
+
+ if( strstr( psz_result, "Disk Ejected" ) != NULL )
+ {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ Return the size of the CD in logical block address (LBA) units.
+ */
+static uint32_t
+_stat_size_osx (void *user_data)
+{
+ return get_track_lba_osx(user_data, CDIO_CDROM_LEADOUT_TRACK);
+}
+
+/*!
+ Return the value associated with the key "arg".
+*/
+static const char *
+_get_arg_osx (void *user_data, const char key[])
+{
+ _img_private_t *p_env = user_data;
+
+ if (!strcmp (key, "source")) {
+ return p_env->gen.source_name;
+ } else if (!strcmp (key, "access-mode")) {
+ switch (p_env->access_mode) {
+ case _AM_OSX:
+ return "OS X";
+ case _AM_NONE:
+ return "no access method";
+ }
+ }
+ return NULL;
+}
+
+/*!
+ Return the media catalog number MCN.
+ */
+static char *
+get_mcn_osx (const void *user_data) {
+ const _img_private_t *p_env = user_data;
+ dk_cd_read_mcn_t cd_read;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ if( ioctl( p_env->gen.fd, DKIOCCDREADMCN, &cd_read ) < 0 )
+ {
+ cdio_debug( "could not read MCN, %s", strerror(errno) );
+ return NULL;
+ }
+ return strdup((char*)cd_read.mcn);
+}
+
+
+/*!
+ Get format of track.
+*/
+static track_format_t
+get_track_format_osx(void *user_data, track_t i_track)
+{
+ _img_private_t *p_env = user_data;
+ dk_cd_read_track_info_t cd_read;
+ CDTrackInfo a_track;
+
+ if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
+
+ if (i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track)
+ return TRACK_FORMAT_ERROR;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ cd_read.address = i_track;
+ cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber;
+
+ cd_read.buffer = &a_track;
+ cd_read.bufferLength = sizeof(CDTrackInfo);
+
+ if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 )
+ {
+ cdio_warn( "could not read trackinfo for track %d", i_track );
+ return TRACK_FORMAT_ERROR;
+ }
+
+ cdio_debug( "%d: trackinfo trackMode: %x dataMode: %x", i_track, a_track.trackMode, a_track.dataMode );
+
+ if (a_track.trackMode == CDIO_CDROM_DATA_TRACK) {
+ if (a_track.dataMode == CDROM_CDI_TRACK) {
+ return TRACK_FORMAT_CDI;
+ } else if (a_track.dataMode == CDROM_XA_TRACK) {
+ return TRACK_FORMAT_XA;
+ } else {
+ return TRACK_FORMAT_DATA;
+ }
+ } else {
+ return TRACK_FORMAT_AUDIO;
+ }
+
+}
+
+/*!
+ Return true if we have XA data (green, mode2 form1) or
+ XA data (green, mode2 form2). That is track begins:
+ sync - header - subheader
+ 12 4 - 8
+
+ FIXME: there's gotta be a better design for this and get_track_format?
+*/
+static bool
+get_track_green_osx(void *user_data, track_t i_track)
+{
+ _img_private_t *p_env = user_data;
+ CDTrackInfo a_track;
+
+ if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
+
+ if ( i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track )
+ return false;
+
+ else {
+
+ dk_cd_read_track_info_t cd_read;
+
+ memset( &cd_read, 0, sizeof(cd_read) );
+
+ cd_read.address = i_track;
+ cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber;
+
+ cd_read.buffer = &a_track;
+ cd_read.bufferLength = sizeof(CDTrackInfo);
+
+ if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) {
+ cdio_warn( "could not read trackinfo for track %d", i_track );
+ return false;
+ }
+ return ((a_track.trackMode & CDIO_CDROM_DATA_TRACK) != 0);
+ }
+}
+
+#endif /* HAVE_DARWIN_CDROM */
+
+/*!
+ Return a string containing the default CD device if none is specified.
+ */
+char **
+cdio_get_devices_osx(void)
+{
+#ifndef HAVE_DARWIN_CDROM
+ return NULL;
+#else
+ io_object_t next_media;
+ mach_port_t master_port;
+ kern_return_t kern_result;
+ io_iterator_t media_iterator;
+ CFMutableDictionaryRef classes_to_match;
+ char **drives = NULL;
+ unsigned int num_drives=0;
+
+ kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
+ if( kern_result != KERN_SUCCESS )
+ {
+ return( NULL );
+ }
+
+ classes_to_match = IOServiceMatching( kIOCDMediaClass );
+ if( classes_to_match == NULL )
+ {
+ return( NULL );
+ }
+
+ CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
+ kCFBooleanTrue );
+
+ kern_result = IOServiceGetMatchingServices( master_port,
+ classes_to_match,
+ &media_iterator );
+ if( kern_result != KERN_SUCCESS )
+ {
+ return( NULL );
+ }
+
+ next_media = IOIteratorNext( media_iterator );
+ if( next_media != 0 )
+ {
+ char psz_buf[0x32];
+ size_t dev_path_length;
+ CFTypeRef str_bsd_path;
+
+ do
+ {
+ str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
+ CFSTR( kIOBSDNameKey ),
+ kCFAllocatorDefault,
+ 0 );
+ if( str_bsd_path == NULL )
+ {
+ IOObjectRelease( next_media );
+ continue;
+ }
+
+ snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
+ dev_path_length = strlen( psz_buf );
+
+ if( CFStringGetCString( str_bsd_path,
+ (char*)&psz_buf + dev_path_length,
+ sizeof(psz_buf) - dev_path_length,
+ kCFStringEncodingASCII ) )
+ {
+ CFRelease( str_bsd_path );
+ IOObjectRelease( next_media );
+ IOObjectRelease( media_iterator );
+ cdio_add_device_list(&drives, strdup(psz_buf), &num_drives);
+ }
+
+ CFRelease( str_bsd_path );
+ IOObjectRelease( next_media );
+
+ } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
+ }
+ IOObjectRelease( media_iterator );
+ cdio_add_device_list(&drives, NULL, &num_drives);
+ return drives;
+#endif /* HAVE_DARWIN_CDROM */
+}
+
+/*!
+ Return a string containing the default CD device if none is specified.
+ */
+char *
+cdio_get_default_device_osx(void)
+{
+#ifndef HAVE_DARWIN_CDROM
+ return NULL;
+#else
+ io_object_t next_media;
+ mach_port_t master_port;
+ kern_return_t kern_result;
+ io_iterator_t media_iterator;
+ CFMutableDictionaryRef classes_to_match;
+
+ kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
+ if( kern_result != KERN_SUCCESS )
+ {
+ return( NULL );
+ }
+
+ classes_to_match = IOServiceMatching( kIOCDMediaClass );
+ if( classes_to_match == NULL )
+ {
+ return( NULL );
+ }
+
+ CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
+ kCFBooleanTrue );
+
+ kern_result = IOServiceGetMatchingServices( master_port,
+ classes_to_match,
+ &media_iterator );
+ if( kern_result != KERN_SUCCESS )
+ {
+ return( NULL );
+ }
+
+ next_media = IOIteratorNext( media_iterator );
+ if( next_media != 0 )
+ {
+ char psz_buf[0x32];
+ size_t dev_path_length;
+ CFTypeRef str_bsd_path;
+
+ do
+ {
+ str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
+ CFSTR( kIOBSDNameKey ),
+ kCFAllocatorDefault,
+ 0 );
+ if( str_bsd_path == NULL )
+ {
+ IOObjectRelease( next_media );
+ continue;
+ }
+
+ snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
+ dev_path_length = strlen( psz_buf );
+
+ if( CFStringGetCString( str_bsd_path,
+ (char*)&psz_buf + dev_path_length,
+ sizeof(psz_buf) - dev_path_length,
+ kCFStringEncodingASCII ) )
+ {
+ CFRelease( str_bsd_path );
+ IOObjectRelease( next_media );
+ IOObjectRelease( media_iterator );
+ return strdup( psz_buf );
+ }
+
+ CFRelease( str_bsd_path );
+ IOObjectRelease( next_media );
+
+ } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
+ }
+ IOObjectRelease( media_iterator );
+ return NULL;
+#endif /* HAVE_DARWIN_CDROM */
+}
+
+/*!
+ Initialization routine. This is the only thing that doesn't
+ get called via a function pointer. In fact *we* are the
+ ones to set that up.
+ */
+CdIo *
+cdio_open_am_osx (const char *psz_source_name, const char *psz_access_mode)
+{
+
+ if (psz_access_mode != NULL)
+ cdio_warn ("there is only one access mode for OS X. Arg %s ignored",
+ psz_access_mode);
+ return cdio_open_osx(psz_source_name);
+}
+
+
+/*!
+ Initialization routine. This is the only thing that doesn't
+ get called via a function pointer. In fact *we* are the
+ ones to set that up.
+ */
+CdIo *
+cdio_open_osx (const char *psz_orig_source)
+{
+
+#ifdef HAVE_DARWIN_CDROM
+ CdIo *ret;
+ _img_private_t *_data;
+ char *psz_source;
+
+ cdio_funcs _funcs = {
+ .eject_media = _eject_media_osx,
+ .free = _free_osx,
+ .get_arg = _get_arg_osx,
+ .get_cdtext = get_cdtext_osx,
+ .get_default_device = cdio_get_default_device_osx,
+ .get_devices = cdio_get_devices_osx,
+ .get_discmode = get_discmode_osx,
+ .get_drive_cap = get_drive_cap_osx,
+ .get_first_track_num= get_first_track_num_generic,
+ .get_hwinfo = get_hwinfo_osx,
+ .get_mcn = get_mcn_osx,
+ .get_num_tracks = get_num_tracks_generic,
+ .get_track_format = get_track_format_osx,
+ .get_track_green = get_track_green_osx,
+ .get_track_lba = get_track_lba_osx,
+ .get_track_msf = NULL,
+ .lseek = cdio_generic_lseek,
+ .read = cdio_generic_read,
+ .read_audio_sectors = _get_read_audio_sectors_osx,
+ .read_mode1_sector = _get_read_mode1_sector_osx,
+ .read_mode1_sectors = _get_read_mode1_sectors_osx,
+ .read_mode2_sector = _get_read_mode2_sector_osx,
+ .read_mode2_sectors = _get_read_mode2_sectors_osx,
+ .read_toc = read_toc_osx,
+ .run_scsi_mmc_cmd = run_scsi_cmd_osx,
+ .set_arg = _set_arg_osx,
+ .stat_size = _stat_size_osx
+ };
+
+ _data = _cdio_malloc (sizeof (_img_private_t));
+ _data->access_mode = _AM_OSX;
+ _data->MediaClass_service = 0;
+ _data->gen.init = false;
+ _data->gen.fd = -1;
+ _data->gen.toc_init = false;
+ _data->gen.b_cdtext_init = false;
+ _data->gen.b_cdtext_error = false;
+
+ if (NULL == psz_orig_source) {
+ psz_source=cdio_get_default_device_osx();
+ if (NULL == psz_source) return NULL;
+ _set_arg_osx(_data, "source", psz_source);
+ free(psz_source);
+ } else {
+ if (cdio_is_device_generic(psz_orig_source))
+ _set_arg_osx(_data, "source", psz_orig_source);
+ else {
+ /* The below would be okay if all device drivers worked this way. */
+#if 0
+ cdio_info ("source %s is a not a device", psz_orig_source);
+#endif
+ return NULL;
+ }
+ }
+
+ ret = cdio_new ((void *)_data, &_funcs);
+ if (ret == NULL) return NULL;
+
+ if (cdio_generic_init(_data) && init_osx(_data))
+ return ret;
+ else {
+ cdio_generic_free (_data);
+ return NULL;
+ }
+
+#else
+ return NULL;
+#endif /* HAVE_DARWIN_CDROM */
+
+}
+
+bool
+cdio_have_osx (void)
+{
+#ifdef HAVE_DARWIN_CDROM
+ return true;
+#else
+ return false;
+#endif /* HAVE_DARWIN_CDROM */
+}