Blame lib/driver/osx.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2003-2006, 2008, 2010-2012, 2014-2015, 2017
Packit dd8086
   Rocky Bernstein <rocky@gnu.org>
Packit dd8086
  from vcdimager code:
Packit dd8086
  Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
Packit dd8086
  and VideoLAN code Copyright (C) 1998-2001 VideoLAN
Packit dd8086
      Authors: Johan Bilien <jobi@via.ecp.fr>
Packit dd8086
               Gildas Bazin <gbazin@netcourrier.com>
Packit dd8086
               Jon Lech Johansen <jon-vl@nanocrew.net>
Packit dd8086
               Derk-Jan Hartman <hartman at videolan.org>
Packit dd8086
               Justin F. Hallett <thesin@southofheaven.org>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* This file contains OSX-specific code and implements low-level
Packit dd8086
   control of the CD drive.
Packit dd8086
*/
Packit dd8086

Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
# include "config.h"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDBOOL_H
Packit dd8086
# include <stdbool.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/sector.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
Packit dd8086
/* For SCSI TR_* enumerations */
Packit dd8086
typedef enum {
Packit dd8086
  TR_OK =            0,
Packit dd8086
  TR_EWRITE =        1  /**< Error writing packet command (transport) */,
Packit dd8086
  TR_EREAD =         2  /**< Error reading packet data (transport) */,
Packit dd8086
  TR_UNDERRUN =      3  /**< Read underrun */,
Packit dd8086
  TR_OVERRUN =       4  /**< Read overrun */,
Packit dd8086
  TR_ILLEGAL =       5  /**< Illegal/rejected request */,
Packit dd8086
  TR_MEDIUM =        6  /**< Medium error */,
Packit dd8086
  TR_BUSY =          7  /**< Device busy */,
Packit dd8086
  TR_NOTREADY =      8  /**< Device not ready */,
Packit dd8086
  TR_FAULT =         9  /**< Device failure */,
Packit dd8086
  TR_UNKNOWN =      10  /**< Unspecified error */,
Packit dd8086
  TR_STREAMING =    11  /**< loss of streaming */,
Packit dd8086
} transport_error_t;
Packit dd8086
Packit dd8086
#include "cdio_assert.h"
Packit dd8086
#include "cdio_private.h"
Packit dd8086
Packit dd8086
#include <string.h>
Packit dd8086
Packit dd8086
#ifdef HAVE_DARWIN_CDROM
Packit dd8086
#undef VERSION
Packit dd8086
Packit dd8086
#include <CoreFoundation/CoreFoundation.h>
Packit dd8086
#include <IOKit/IOKitLib.h>
Packit dd8086
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
Packit dd8086
Packit dd8086
#include <mach/mach.h>
Packit dd8086
#include <Carbon/Carbon.h>
Packit dd8086
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1030
Packit dd8086
# include <IOKit/scsi/SCSITaskLib.h>
Packit dd8086
#else
Packit dd8086
# include <IOKit/scsi-commands/SCSITaskLib.h>
Packit dd8086
#endif
Packit dd8086
#include <IOKit/IOCFPlugIn.h>
Packit dd8086
#include <mach/mach_error.h>
Packit dd8086
Packit dd8086
#include <stdio.h>
Packit dd8086
#include <stdlib.h>
Packit dd8086
#include <errno.h>
Packit dd8086
#include <unistd.h>
Packit dd8086
#include <fcntl.h>
Packit dd8086
Packit dd8086
#include <sys/stat.h>
Packit dd8086
#include <sys/types.h>
Packit dd8086
#include <sys/ioctl.h>
Packit dd8086
Packit dd8086
#include <paths.h>
Packit dd8086
#include <CoreFoundation/CoreFoundation.h>
Packit dd8086
#include <IOKit/IOKitLib.h>
Packit dd8086
#include <IOKit/IOBSD.h>
Packit dd8086
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1030
Packit dd8086
# include <IOKit/scsi/IOSCSIMultimediaCommandsDevice.h>
Packit dd8086
#else
Packit dd8086
# include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h>
Packit dd8086
#endif
Packit dd8086
#include <IOKit/storage/IOCDTypes.h>
Packit dd8086
#include <IOKit/storage/IODVDTypes.h>
Packit dd8086
#include <IOKit/storage/IOMedia.h>
Packit dd8086
#include <IOKit/storage/IOCDMedia.h>
Packit dd8086
#include <IOKit/storage/IODVDMedia.h>
Packit dd8086
#include <IOKit/storage/IOCDMediaBSDClient.h>
Packit dd8086
#include <IOKit/storage/IODVDMediaBSDClient.h>
Packit dd8086
#include <IOKit/storage/IOBlockStorageDevice.h>
Packit dd8086
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
Packit dd8086
Packit dd8086
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
Packit dd8086
#include <IOKit/storage/IOBDTypes.h>
Packit dd8086
#include <IOKit/storage/IOBDMedia.h>
Packit dd8086
#include <IOKit/storage/IOBDMediaBSDClient.h>
Packit dd8086
#else
Packit dd8086
#define kIOBDMediaClass "IOBDMedia" // It does not hurt, simplyfies rest of code
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_DISKARBITRATION
Packit dd8086
#include <DiskArbitration/DiskArbitration.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* FIXME */
Packit dd8086
#define MAX_BIG_BUFF_SIZE  65535
Packit dd8086
Packit dd8086
#define kIOCDBlockStorageDeviceClassString              "IOCDBlockStorageDevice"
Packit dd8086
Packit dd8086
/* Note leadout is normally defined 0xAA, But on OSX 0xA0 is "lead in" while
Packit dd8086
   0xA2 is "lead out". I don't understand the distinction, and therefore
Packit dd8086
   something could be wrong. */
Packit dd8086
#define OSX_CDROM_LEADOUT_TRACK 0xA2
Packit dd8086
Packit dd8086
#define TOTAL_TRACKS    (p_env->i_last_track - p_env->gen.i_first_track + 1)
Packit dd8086
Packit dd8086
#define CDROM_CDI_TRACK 0x1
Packit dd8086
#define CDROM_XA_TRACK  0x2
Packit dd8086
Packit dd8086
typedef enum {
Packit dd8086
  _AM_NONE,
Packit dd8086
  _AM_OSX,
Packit dd8086
} access_mode_t;
Packit dd8086
Packit dd8086
#define MAX_SERVICE_NAME 1000
Packit dd8086
typedef struct {
Packit dd8086
  /* Things common to all drivers like this.
Packit dd8086
     This must be first. */
Packit dd8086
  generic_img_private_t gen;
Packit dd8086
Packit dd8086
  access_mode_t access_mode;
Packit dd8086
Packit dd8086
  /* Track information */
Packit dd8086
  CDTOC *pTOC;
Packit dd8086
  int i_descriptors;
Packit dd8086
  track_t i_last_track;      /* highest track number */
Packit dd8086
  track_t i_last_session;    /* highest session number */
Packit dd8086
  track_t i_first_session;   /* first session number */
Packit dd8086
  lsn_t   *pp_lba;
Packit dd8086
  io_service_t MediaClass_service;
Packit dd8086
  char    psz_MediaClass_service[MAX_SERVICE_NAME];
Packit dd8086
  SCSITaskDeviceInterface **pp_scsiTaskDeviceInterface;
Packit dd8086
Packit dd8086
  // io_service_t obj;
Packit dd8086
  // SCSITaskDeviceInterface **scsi;
Packit dd8086
  SCSITaskInterface **scsi_task;
Packit dd8086
  MMCDeviceInterface **mmc;
Packit dd8086
  IOCFPlugInInterface **plugin;
Packit dd8086
Packit dd8086
  SCSI_Sense_Data sense;
Packit dd8086
  SCSITaskStatus status;
Packit dd8086
  UInt64 realized_len;
Packit dd8086
Packit dd8086
Packit dd8086
} _img_private_t;
Packit dd8086
Packit dd8086
static bool read_toc_osx (void *p_user_data);
Packit dd8086
static track_format_t get_track_format_osx(void *p_user_data,
Packit dd8086
                                           track_t i_track);
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * GetRegistryEntryProperties - Gets the registry entry properties for
Packit dd8086
 *  an io_service_t.
Packit dd8086
 */
Packit dd8086
Packit dd8086
static CFMutableDictionaryRef
Packit dd8086
GetRegistryEntryProperties ( io_service_t service )
Packit dd8086
{
Packit dd8086
  IOReturn                      err     = kIOReturnSuccess;
Packit dd8086
  CFMutableDictionaryRef        dict    = 0;
Packit dd8086
Packit dd8086
  err = IORegistryEntryCreateCFProperties (service, &dict,
Packit dd8086
                                           kCFAllocatorDefault, 0);
Packit dd8086
  if ( err != kIOReturnSuccess )
Packit dd8086
    cdio_warn( "IORegistryEntryCreateCFProperties: 0x%08x", err );
Packit dd8086
Packit dd8086
  return dict;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * ProbeStorageDevices - Probe devices to detect changes.
Packit dd8086
 */
Packit dd8086
static bool
Packit dd8086
ProbeStorageDevices()
Packit dd8086
{
Packit dd8086
  io_service_t  next_service;
Packit dd8086
  mach_port_t   master_port;
Packit dd8086
  kern_return_t kern_result;
Packit dd8086
  io_iterator_t service_iterator;
Packit dd8086
  CFMutableDictionaryRef classes_to_match;
Packit dd8086
Packit dd8086
  kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
Packit dd8086
  if( kern_result != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  classes_to_match = IOServiceMatching( kIOBlockStorageDeviceClass );
Packit dd8086
  if( classes_to_match == NULL )
Packit dd8086
    {
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  kern_result = IOServiceGetMatchingServices( master_port,
Packit dd8086
                                              classes_to_match,
Packit dd8086
                                              &service_iterator );
Packit dd8086
  if( kern_result != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  next_service = IOIteratorNext( service_iterator );
Packit dd8086
  if( next_service != 0 )
Packit dd8086
    {
Packit dd8086
      do
Packit dd8086
        {
Packit dd8086
          IOServiceRequestProbe( next_service, 0 );
Packit dd8086
Packit dd8086
          IOObjectRelease( next_service );
Packit dd8086
Packit dd8086
        } while( ( next_service = IOIteratorNext( service_iterator ) ) != 0 );
Packit dd8086
    }
Packit dd8086
  IOObjectRelease( service_iterator );
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
#ifdef GET_SCSI_FIXED
Packit dd8086
static bool
Packit dd8086
get_scsi(_img_private_t *p_env)
Packit dd8086
{
Packit dd8086
  SInt32 score;
Packit dd8086
  kern_return_t err;
Packit dd8086
  HRESULT herr;
Packit dd8086
Packit dd8086
  err = IOCreatePlugInInterfaceForService(p_env->MediaClass_service,
Packit dd8086
                                          kIOMMCDeviceUserClientTypeID,
Packit dd8086
                                          kIOCFPlugInInterfaceID,
Packit dd8086
                                          &p_env->plugin,
Packit dd8086
                                          &score);
Packit dd8086
Packit dd8086
  if (err != noErr) {
Packit dd8086
    fprintf(stderr, "Error %x accessing MMC plugin.\n", err);
Packit dd8086
    return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  herr = (*p_env->plugin) ->
Packit dd8086
    QueryInterface(p_env->plugin, CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
Packit dd8086
                   (void *)&p_env->mmc);
Packit dd8086
Packit dd8086
  if (herr != S_OK) {
Packit dd8086
    fprintf(stderr, "Error %x accessing MMC interface.\n", (int) herr);
Packit dd8086
    IODestroyPlugInInterface(p_env->plugin);
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->pp_scsiTaskDeviceInterface =
Packit dd8086
    (*p_env->mmc)->GetSCSITaskDeviceInterface(p_env->mmc);
Packit dd8086
Packit dd8086
  if (!p_env->pp_scsiTaskDeviceInterface) {
Packit dd8086
    fprintf(stderr,
Packit dd8086
            "Could not get SCSITaskkDevice interface from MMC interface.\n");
Packit dd8086
    (*p_env->mmc)->Release(p_env->mmc);
Packit dd8086
    IODestroyPlugInInterface(p_env->plugin);
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  err = (*p_env->pp_scsiTaskDeviceInterface)->
Packit dd8086
    ObtainExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
  if (err != kIOReturnSuccess) {
Packit dd8086
    fprintf(stderr, "Could not obtain exclusive access to the device (%x).\n",
Packit dd8086
            err);
Packit dd8086
Packit dd8086
    if (err == kIOReturnBusy)
Packit dd8086
      fprintf(stderr, "The volume is already mounted.\n");
Packit dd8086
    else if (err == kIOReturnExclusiveAccess)
Packit dd8086
      fprintf(stderr, "Another application already has exclusive access "
Packit dd8086
              "to this device.\n");
Packit dd8086
    else
Packit dd8086
      fprintf(stderr, "I don't know why.\n");
Packit dd8086
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface)->
Packit dd8086
      Release(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
    (*p_env->mmc)->Release(p_env->mmc);
Packit dd8086
    IODestroyPlugInInterface(p_env->plugin);
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->scsi_task =
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface) ->
Packit dd8086
    CreateSCSITask(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
Packit dd8086
  if (!p_env->scsi_task) {
Packit dd8086
    fprintf(stderr, "Could not create a SCSITask interface.\n");
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface)->
Packit dd8086
      ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface)->
Packit dd8086
      Release(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
    (*p_env->mmc)->Release(p_env->mmc);
Packit dd8086
    IODestroyPlugInInterface(p_env->plugin);
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
static bool
Packit dd8086
init_osx(_img_private_t *p_env) {
Packit dd8086
  char *psz_devname;
Packit dd8086
  kern_return_t ret;
Packit dd8086
  io_iterator_t iterator;
Packit dd8086
Packit dd8086
  /* Only open if not already opened. Otherwise, too many descriptors
Packit dd8086
     are holding the device busy.  */
Packit dd8086
  if (-1 == p_env->gen.fd)
Packit dd8086
    p_env->gen.fd = open( p_env->gen.source_name, O_RDONLY | O_NONBLOCK );
Packit dd8086
Packit dd8086
  if (-1 == p_env->gen.fd) {
Packit dd8086
    cdio_warn("Failed to open %s: %s", p_env->gen.source_name,
Packit dd8086
               strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Get the device name. */
Packit dd8086
  psz_devname = strrchr( p_env->gen.source_name, '/');
Packit dd8086
  if( NULL != psz_devname )
Packit dd8086
    ++psz_devname;
Packit dd8086
  else
Packit dd8086
    psz_devname = p_env->gen.source_name;
Packit dd8086
Packit dd8086
  /* Unraw the device name. */
Packit dd8086
  if( *psz_devname == 'r' )
Packit dd8086
    ++psz_devname;
Packit dd8086
Packit dd8086
  ret = IOServiceGetMatchingServices( kIOMasterPortDefault,
Packit dd8086
                                      IOBSDNameMatching(kIOMasterPortDefault,
Packit dd8086
                                                        0, psz_devname),
Packit dd8086
                                      &iterator );
Packit dd8086
Packit dd8086
  /* Get service iterator for the device. */
Packit dd8086
  if( ret != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
        cdio_warn( "IOServiceGetMatchingServices: 0x%08x", ret );
Packit dd8086
        return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  /* first service */
Packit dd8086
  p_env->MediaClass_service = IOIteratorNext( iterator );
Packit dd8086
  IOObjectRelease( iterator );
Packit dd8086
Packit dd8086
  /* search for kIOCDMediaClass or kIODVDMediaClass or kIOBDMediaClass */
Packit dd8086
  while( p_env->MediaClass_service &&
Packit dd8086
         (!IOObjectConformsTo(p_env->MediaClass_service, kIOCDMediaClass)) &&
Packit dd8086
         (!IOObjectConformsTo(p_env->MediaClass_service, kIODVDMediaClass)) &&
Packit dd8086
         (!IOObjectConformsTo(p_env->MediaClass_service, kIOBDMediaClass)) )
Packit dd8086
    {
Packit dd8086
Packit dd8086
      ret = IORegistryEntryGetParentIterator( p_env->MediaClass_service,
Packit dd8086
                                              kIOServicePlane,
Packit dd8086
                                              &iterator );
Packit dd8086
      if( ret != KERN_SUCCESS )
Packit dd8086
        {
Packit dd8086
          cdio_warn( "IORegistryEntryGetParentIterator: 0x%08x", ret );
Packit dd8086
          IOObjectRelease( p_env->MediaClass_service );
Packit dd8086
          return false;
Packit dd8086
        }
Packit dd8086
Packit dd8086
      IOObjectRelease( p_env->MediaClass_service );
Packit dd8086
Packit dd8086
      p_env->MediaClass_service = IOIteratorNext( iterator );
Packit dd8086
      IOObjectRelease( iterator );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  if ( 0 == p_env->MediaClass_service )     {
Packit dd8086
    cdio_warn( "search for kIOCDMediaClass/kIODVDMediaClass/kIOBDMediaClass came up empty" );
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Save the name so we can compare against this in case we have to do
Packit dd8086
     another scan. FIXME: this is hoaky and there's got to be a better
Packit dd8086
     variable to test or way to do.
Packit dd8086
   */
Packit dd8086
  IORegistryEntryGetPath(p_env->MediaClass_service, kIOServicePlane,
Packit dd8086
                         p_env->psz_MediaClass_service);
Packit dd8086
#ifdef GET_SCSI_FIXED
Packit dd8086
  return get_scsi(p_env);
Packit dd8086
#else
Packit dd8086
  return true;
Packit dd8086
#endif
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Run a SCSI MMC command.
Packit dd8086
Packit dd8086
  cdio          CD structure set by cdio_open().
Packit dd8086
  i_timeout     time in milliseconds we will wait for the command
Packit dd8086
                to complete. If this value is -1, use the default
Packit dd8086
                time-out value.
Packit dd8086
  p_buf         Buffer for data, both sending and receiving
Packit dd8086
  i_buf         Size of buffer
Packit dd8086
  e_direction   direction the transfer is to go.
Packit dd8086
  cdb           CDB bytes. All values that are needed should be set on
Packit dd8086
                input. We'll figure out what the right CDB length should be.
Packit dd8086
Packit dd8086
  We return true if command completed successfully and false if not.
Packit dd8086
 */
Packit dd8086
#if 1
Packit dd8086
Packit dd8086
/* process a complete scsi command. */
Packit dd8086
static int
Packit dd8086
run_mmc_cmd_osx( void *p_user_data,
Packit dd8086
                 unsigned int i_timeout_ms,
Packit dd8086
                 unsigned int i_cdb, const mmc_cdb_t *p_cdb,
Packit dd8086
                 cdio_mmc_direction_t e_direction,
Packit dd8086
                 unsigned int i_buf, /*in/out*/ void *p_buf )
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  uint8_t cmdbuf[16];
Packit dd8086
  UInt8 dir;
Packit dd8086
  IOVirtualRange buf;
Packit dd8086
  IOReturn ret;
Packit dd8086
Packit dd8086
  if (!p_env->scsi_task) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
Packit dd8086
  p_env->gen.scsi_mmc_sense_valid = 0;
Packit dd8086
  memcpy(cmdbuf, p_cdb, i_cdb);
Packit dd8086
Packit dd8086
  dir =
Packit dd8086
      (SCSI_MMC_DATA_READ == e_direction)
Packit dd8086
      ? kSCSIDataTransfer_FromTargetToInitiator :
Packit dd8086
      (SCSI_MMC_DATA_WRITE == e_direction)
Packit dd8086
      ? kSCSIDataTransfer_FromInitiatorToTarget
Packit dd8086
      : kSCSIDataTransfer_NoDataTransfer;
Packit dd8086
Packit dd8086
  if (!i_buf)
Packit dd8086
    dir = kSCSIDataTransfer_NoDataTransfer;
Packit dd8086
Packit dd8086
  if (i_buf > MAX_BIG_BUFF_SIZE) {
Packit dd8086
    fprintf(stderr, "Excessive request size: %d bytes\n", i_buf);
Packit dd8086
    return TR_ILLEGAL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  buf.address = (IOVirtualAddress)p_buf;
Packit dd8086
  buf.length = i_buf;
Packit dd8086
Packit dd8086
  ret = (*p_env->scsi_task)->SetCommandDescriptorBlock(p_env->scsi_task,
Packit dd8086
                                                       cmdbuf, i_cdb);
Packit dd8086
  if (ret != kIOReturnSuccess) {
Packit dd8086
    fprintf(stderr, "SetCommandDescriptorBlock: %x\n", ret);
Packit dd8086
    return TR_UNKNOWN;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret = (*p_env->scsi_task)->SetScatterGatherEntries(p_env->scsi_task, &buf, 1,
Packit dd8086
                                                     i_buf, dir);
Packit dd8086
  if (ret != kIOReturnSuccess) {
Packit dd8086
    fprintf(stderr, "SetScatterGatherEntries: %x\n", ret);
Packit dd8086
    return TR_UNKNOWN;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret = (*p_env->scsi_task)->ExecuteTaskSync(p_env->scsi_task, &p_env->sense,
Packit dd8086
                                             &p_env->status,
Packit dd8086
                                             &p_env->realized_len);
Packit dd8086
  if (ret != kIOReturnSuccess) {
Packit dd8086
    fprintf(stderr, "ExecuteTaskSync: %x\n", ret);
Packit dd8086
    return TR_UNKNOWN;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (p_env->status != kSCSITaskStatus_GOOD) {
Packit dd8086
    int i;
Packit dd8086
Packit dd8086
    fprintf(stderr, "SCSI status: %x\n", p_env->status);
Packit dd8086
    fprintf(stderr, "Sense: %x %x %x\n",
Packit dd8086
            p_env->sense.SENSE_KEY,
Packit dd8086
            p_env->sense.ADDITIONAL_SENSE_CODE,
Packit dd8086
            p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER);
Packit dd8086
Packit dd8086
    for (i = 0; i < i_cdb; i++)
Packit dd8086
      fprintf(stderr, "%02x ", cmdbuf[i]);
Packit dd8086
Packit dd8086
    fprintf(stderr, "\n");
Packit dd8086
    memcpy((void *) p_env->gen.scsi_mmc_sense, &p_env->sense, kSenseDefaultSize);
Packit dd8086
Packit dd8086
    return TR_UNKNOWN;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (p_env->sense.VALID_RESPONSE_CODE) {
Packit dd8086
    char key = p_env->sense.SENSE_KEY & 0xf;
Packit dd8086
    char ASC = p_env->sense.ADDITIONAL_SENSE_CODE;
Packit dd8086
    char ASCQ = p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER;
Packit dd8086
Packit dd8086
    switch (key) {
Packit dd8086
    case 0:
Packit dd8086
      if (errno == 0)
Packit dd8086
        errno = EIO;
Packit dd8086
      return (TR_UNKNOWN);
Packit dd8086
    case 1:
Packit dd8086
      break;
Packit dd8086
    case 2:
Packit dd8086
      if (errno == 0)
Packit dd8086
        errno = EBUSY;
Packit dd8086
      return (TR_BUSY);
Packit dd8086
    case 3:
Packit dd8086
      if (ASC == 0x0C && ASCQ == 0x09) {
Packit dd8086
        /* loss of streaming */
Packit dd8086
        if (errno == 0)
Packit dd8086
          errno = EIO;
Packit dd8086
        return (TR_STREAMING);
Packit dd8086
      } else {
Packit dd8086
        if (errno == 0)
Packit dd8086
          errno = EIO;
Packit dd8086
        return (TR_MEDIUM);
Packit dd8086
      }
Packit dd8086
    case 4:
Packit dd8086
      if (errno == 0)
Packit dd8086
        errno = EIO;
Packit dd8086
      return (TR_FAULT);
Packit dd8086
    case 5:
Packit dd8086
      if (errno == 0)
Packit dd8086
        errno = EINVAL;
Packit dd8086
      return (TR_ILLEGAL);
Packit dd8086
    default:
Packit dd8086
      if (errno == 0)
Packit dd8086
        errno = EIO;
Packit dd8086
      return (TR_UNKNOWN);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  errno = 0;
Packit dd8086
  return (0);
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#if 0
Packit dd8086
/**
Packit dd8086
  Run a SCSI MMC command.
Packit dd8086
Packit dd8086
  cdio          CD structure set by cdio_open().
Packit dd8086
  i_timeout     time in milliseconds we will wait for the command
Packit dd8086
                to complete. If this value is -1, use the default
Packit dd8086
                time-out value.
Packit dd8086
  p_buf         Buffer for data, both sending and receiving
Packit dd8086
  i_buf         Size of buffer
Packit dd8086
  e_direction   direction the transfer is to go.
Packit dd8086
  cdb           CDB bytes. All values that are needed should be set on
Packit dd8086
                input. We'll figure out what the right CDB length should be.
Packit dd8086
Packit dd8086
  We return true if command completed successfully and false if not.
Packit dd8086
 */
Packit dd8086
static int
Packit dd8086
run_mmc_cmd_osx( const void *p_user_data,
Packit dd8086
                 unsigned int i_timeout_ms,
Packit dd8086
                 unsigned int i_cdb, const mmc_cdb_t *p_cdb,
Packit dd8086
                 cdio_mmc_direction_t e_direction,
Packit dd8086
                 unsigned int i_buf, /*in/out*/ void *p_buf )
Packit dd8086
{
Packit dd8086
Packit dd8086
#ifndef SCSI_MMC_FIXED
Packit dd8086
  return DRIVER_OP_UNSUPPORTED;
Packit dd8086
#else
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  SCSITaskDeviceInterface **sc;
Packit dd8086
  SCSITaskInterface **cmd = NULL;
Packit dd8086
  IOVirtualRange iov;
Packit dd8086
  SCSI_Sense_Data senseData;
Packit dd8086
  SCSITaskStatus status;
Packit dd8086
  UInt64 bytesTransferred;
Packit dd8086
  IOReturn ioReturnValue;
Packit dd8086
  int ret = 0;
Packit dd8086
Packit dd8086
  if (NULL == p_user_data) return 2;
Packit dd8086
Packit dd8086
  /* Make sure pp_scsiTaskDeviceInterface is initialized. FIXME: The code
Packit dd8086
     should probably be reorganized better for this. */
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_osx (p_user_data) ;
Packit dd8086
Packit dd8086
  sc = p_env->pp_scsiTaskDeviceInterface;
Packit dd8086
Packit dd8086
  if (NULL == sc) return 3;
Packit dd8086
Packit dd8086
  cmd = (*sc)->CreateSCSITask(sc);
Packit dd8086
  if (cmd == NULL) {
Packit dd8086
    cdio_warn("Failed to create SCSI task");
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  iov.address = (IOVirtualAddress) p_buf;
Packit dd8086
  iov.length = i_buf;
Packit dd8086
Packit dd8086
  ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd, (UInt8 *) p_cdb,
Packit dd8086
                                                    i_cdb);
Packit dd8086
  if (ioReturnValue != kIOReturnSuccess) {
Packit dd8086
    cdio_warn("SetCommandDescriptorBlock failed with status %x",
Packit dd8086
              ioReturnValue);
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, i_buf,
Packit dd8086
                                                  (SCSI_MMC_DATA_READ == e_direction ) ?
Packit dd8086
                                                  kSCSIDataTransfer_FromTargetToInitiator :
Packit dd8086
                                                  kSCSIDataTransfer_FromInitiatorToTarget);
Packit dd8086
  if (ioReturnValue != kIOReturnSuccess) {
Packit dd8086
    cdio_warn("SetScatterGatherEntries failed with status %x", ioReturnValue);
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, i_timeout_ms );
Packit dd8086
  if (ioReturnValue != kIOReturnSuccess) {
Packit dd8086
    cdio_warn("SetTimeoutDuration failed with status %x", ioReturnValue);
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  memset(&senseData, 0, sizeof(senseData));
Packit dd8086
Packit dd8086
  ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,&senseData, &status, &
Packit dd8086
                                          bytesTransferred);
Packit dd8086
Packit dd8086
  if (ioReturnValue != kIOReturnSuccess) {
Packit dd8086
    cdio_warn("Command execution failed with status %x", ioReturnValue);
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (cmd != NULL) {
Packit dd8086
    (*cmd)->Release(cmd);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return (ret);
Packit dd8086
#endif
Packit dd8086
}
Packit dd8086
#endif /* 0*/
Packit dd8086
Packit dd8086
/***************************************************************************
Packit dd8086
 * GetDeviceIterator - Gets an io_iterator_t for our class type
Packit dd8086
 ***************************************************************************/
Packit dd8086
Packit dd8086
static io_iterator_t
Packit dd8086
GetDeviceIterator ( const char * deviceClass )
Packit dd8086
{
Packit dd8086
Packit dd8086
  IOReturn      err      = kIOReturnSuccess;
Packit dd8086
  io_iterator_t iterator = MACH_PORT_NULL;
Packit dd8086
Packit dd8086
  err = IOServiceGetMatchingServices ( kIOMasterPortDefault,
Packit dd8086
                                       IOServiceMatching ( deviceClass ),
Packit dd8086
                                       &iterator );
Packit dd8086
  cdio_assert ( err == kIOReturnSuccess );
Packit dd8086
Packit dd8086
  return iterator;
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/***************************************************************************
Packit dd8086
 * GetFeaturesFlagsForDrive -Gets the bitfield which represents the
Packit dd8086
 * features flags.
Packit dd8086
 ***************************************************************************/
Packit dd8086
Packit dd8086
static bool
Packit dd8086
GetFeaturesFlagsForDrive ( CFDictionaryRef dict,
Packit dd8086
                           uint32_t *i_cdFlags,
Packit dd8086
                           uint32_t *i_dvdFlags )
Packit dd8086
{
Packit dd8086
  CFDictionaryRef propertiesDict = 0;
Packit dd8086
  CFNumberRef     flagsNumberRef = 0;
Packit dd8086
Packit dd8086
  *i_cdFlags = 0;
Packit dd8086
  *i_dvdFlags= 0;
Packit dd8086
Packit dd8086
  propertiesDict = ( CFDictionaryRef )
Packit dd8086
    CFDictionaryGetValue ( dict,
Packit dd8086
                           CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
Packit dd8086
Packit dd8086
  if ( propertiesDict == 0 ) return false;
Packit dd8086
Packit dd8086
  /* Get the CD features */
Packit dd8086
  flagsNumberRef = ( CFNumberRef )
Packit dd8086
    CFDictionaryGetValue ( propertiesDict,
Packit dd8086
                           CFSTR ( kIOPropertySupportedCDFeatures ) );
Packit dd8086
  if ( flagsNumberRef != 0 ) {
Packit dd8086
    CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_cdFlags );
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Get the DVD features */
Packit dd8086
  flagsNumberRef = ( CFNumberRef )
Packit dd8086
    CFDictionaryGetValue ( propertiesDict,
Packit dd8086
                           CFSTR ( kIOPropertySupportedDVDFeatures ) );
Packit dd8086
  if ( flagsNumberRef != 0 ) {
Packit dd8086
    CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_dvdFlags );
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get disc type associated with the cd object.
Packit dd8086
*/
Packit dd8086
static discmode_t
Packit dd8086
get_discmode_osx (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  char str[10];
Packit dd8086
  int32_t i_discmode = CDIO_DISC_MODE_ERROR;
Packit dd8086
  CFDictionaryRef propertiesDict = 0;
Packit dd8086
  CFStringRef data;
Packit dd8086
Packit dd8086
  propertiesDict  = GetRegistryEntryProperties ( p_env->MediaClass_service );
Packit dd8086
Packit dd8086
  if ( propertiesDict == 0 ) return i_discmode;
Packit dd8086
Packit dd8086
  data = ( CFStringRef )
Packit dd8086
    CFDictionaryGetValue ( propertiesDict, CFSTR ( kIODVDMediaTypeKey ) );
Packit dd8086
Packit dd8086
  if( CFStringGetCString( data, str, sizeof(str),
Packit dd8086
                          kCFStringEncodingASCII ) ) {
Packit dd8086
    if (0 == strncmp(str, "DVD+R", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_PR;
Packit dd8086
    else if (0 == strncmp(str, "DVD+RW", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_PRW;
Packit dd8086
    else if (0 == strncmp(str, "DVD-R", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_R;
Packit dd8086
    else if (0 == strncmp(str, "DVD-RW", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_RW;
Packit dd8086
    else if (0 == strncmp(str, "DVD-ROM", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_ROM;
Packit dd8086
    else if (0 == strncmp(str, "DVD-RAM", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_DVD_RAM;
Packit dd8086
    else if (0 == strncmp(str, "CD-ROM", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
    else if (0 == strncmp(str, "CDR", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
    else if (0 == strncmp(str, "CDRW", strlen(str)) )
Packit dd8086
      i_discmode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
    //??  Handled by below? CFRelease( data );
Packit dd8086
  }
Packit dd8086
  CFRelease( propertiesDict );
Packit dd8086
  if (CDIO_DISC_MODE_CD_DATA == i_discmode) {
Packit dd8086
    /* Need to do more classification */
Packit dd8086
    return get_discmode_cd_generic(p_user_data);
Packit dd8086
  }
Packit dd8086
  return i_discmode;
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
static io_service_t
Packit dd8086
get_drive_service_osx(const _img_private_t *p_env)
Packit dd8086
{
Packit dd8086
  io_service_t  service;
Packit dd8086
  io_iterator_t service_iterator;
Packit dd8086
Packit dd8086
  service_iterator = GetDeviceIterator ( kIOCDBlockStorageDeviceClassString );
Packit dd8086
Packit dd8086
  if( service_iterator == MACH_PORT_NULL ) return 0;
Packit dd8086
Packit dd8086
  service = IOIteratorNext( service_iterator );
Packit dd8086
  if( service == 0 ) return 0;
Packit dd8086
Packit dd8086
  do
Packit dd8086
    {
Packit dd8086
      char psz_service[MAX_SERVICE_NAME];
Packit dd8086
      IORegistryEntryGetPath(service, kIOServicePlane, psz_service);
Packit dd8086
      psz_service[MAX_SERVICE_NAME-1] = '\0';
Packit dd8086
Packit dd8086
      /* FIXME: This is all hoaky. Here we need info from a parent class,
Packit dd8086
         psz_service of what we opened above. We are relying on the
Packit dd8086
         fact that the name  will be a substring of the name we
Packit dd8086
         openned with.
Packit dd8086
      */
Packit dd8086
      if (0 == strncmp(psz_service, p_env->psz_MediaClass_service,
Packit dd8086
                       strlen(psz_service))) {
Packit dd8086
        /* Found our device */
Packit dd8086
        IOObjectRelease( service_iterator );
Packit dd8086
        return service;
Packit dd8086
      }
Packit dd8086
Packit dd8086
      IOObjectRelease( service );
Packit dd8086
Packit dd8086
    } while( ( service = IOIteratorNext( service_iterator ) ) != 0 );
Packit dd8086
Packit dd8086
  IOObjectRelease( service_iterator );
Packit dd8086
  return service;
Packit dd8086
}
Packit dd8086
Packit dd8086
static void
Packit dd8086
get_drive_cap_osx(const void *p_user_data,
Packit dd8086
                  /*out*/ cdio_drive_read_cap_t  *p_read_cap,
Packit dd8086
                  /*out*/ cdio_drive_write_cap_t *p_write_cap,
Packit dd8086
                  /*out*/ cdio_drive_misc_cap_t  *p_misc_cap)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  uint32_t i_cdFlags;
Packit dd8086
  uint32_t i_dvdFlags;
Packit dd8086
Packit dd8086
  io_service_t  service = get_drive_service_osx(p_env);
Packit dd8086
Packit dd8086
  if( service == 0 ) goto err_exit;
Packit dd8086
Packit dd8086
  /* Found our device */
Packit dd8086
  {
Packit dd8086
    CFDictionaryRef  properties = GetRegistryEntryProperties ( service );
Packit dd8086
Packit dd8086
    if (! GetFeaturesFlagsForDrive ( properties, &i_cdFlags,
Packit dd8086
                                     &i_dvdFlags ) ) {
Packit dd8086
      IOObjectRelease( service );
Packit dd8086
      goto err_exit;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /* Reader */
Packit dd8086
Packit dd8086
    if ( 0 != (i_cdFlags & kCDFeaturesAnalogAudioMask) )
Packit dd8086
      *p_read_cap  |= CDIO_DRIVE_CAP_READ_AUDIO;
Packit dd8086
Packit dd8086
    if ( 0 != (i_cdFlags & kCDFeaturesWriteOnceMask) )
Packit dd8086
      *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R;
Packit dd8086
Packit dd8086
    if ( 0 != (i_cdFlags & kCDFeaturesCDDAStreamAccurateMask) )
Packit dd8086
      *p_read_cap  |= CDIO_DRIVE_CAP_READ_CD_DA;
Packit dd8086
Packit dd8086
    if ( 0 != (i_dvdFlags & kDVDFeaturesReadStructuresMask) )
Packit dd8086
      *p_read_cap  |= CDIO_DRIVE_CAP_READ_DVD_ROM;
Packit dd8086
Packit dd8086
    if ( 0 != (i_cdFlags & kCDFeaturesReWriteableMask) )
Packit dd8086
      *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW;
Packit dd8086
Packit dd8086
    if ( 0 != (i_dvdFlags & kDVDFeaturesWriteOnceMask) )
Packit dd8086
      *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R;
Packit dd8086
Packit dd8086
    if ( 0 != (i_dvdFlags & kDVDFeaturesRandomWriteableMask) )
Packit dd8086
      *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM;
Packit dd8086
Packit dd8086
    if ( 0 != (i_dvdFlags & kDVDFeaturesReWriteableMask) )
Packit dd8086
      *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RW;
Packit dd8086
Packit dd8086
    /***
Packit dd8086
        if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRMask) )
Packit dd8086
        *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PR;
Packit dd8086
Packit dd8086
        if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRWMask )
Packit dd8086
        *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PRW;
Packit dd8086
        ***/
Packit dd8086
Packit dd8086
    /* FIXME: fill out. For now assume CD-ROM is relatively modern. */
Packit dd8086
      *p_misc_cap = (
Packit dd8086
                     CDIO_DRIVE_CAP_MISC_CLOSE_TRAY
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_EJECT
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_LOCK
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_SELECT_SPEED
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_MULTI_SESSION
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED
Packit dd8086
                     | CDIO_DRIVE_CAP_MISC_RESET
Packit dd8086
                     | CDIO_DRIVE_CAP_READ_MCN
Packit dd8086
                     | CDIO_DRIVE_CAP_READ_ISRC
Packit dd8086
                     );
Packit dd8086
Packit dd8086
    IOObjectRelease( service );
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return;
Packit dd8086
Packit dd8086
 err_exit:
Packit dd8086
  *p_misc_cap = *p_write_cap = *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN;
Packit dd8086
  return;
Packit dd8086
}
Packit dd8086
Packit dd8086
#if 1
Packit dd8086
/****************************************************************************
Packit dd8086
 * GetDriveDescription - Gets drive description.
Packit dd8086
 ****************************************************************************/
Packit dd8086
Packit dd8086
static bool
Packit dd8086
get_hwinfo_osx ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = (_img_private_t *) p_cdio->env;
Packit dd8086
  io_service_t  service = get_drive_service_osx(p_env);
Packit dd8086
Packit dd8086
  if ( service == 0 ) return false;
Packit dd8086
Packit dd8086
  /* Found our device */
Packit dd8086
  {
Packit dd8086
    CFStringRef      vendor      = NULL;
Packit dd8086
    CFStringRef      product     = NULL;
Packit dd8086
    CFStringRef      revision    = NULL;
Packit dd8086
Packit dd8086
    CFDictionaryRef  properties  = GetRegistryEntryProperties ( service );
Packit dd8086
    CFDictionaryRef  deviceDict  = ( CFDictionaryRef )
Packit dd8086
      CFDictionaryGetValue ( properties,
Packit dd8086
                             CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
Packit dd8086
Packit dd8086
    if ( deviceDict == 0 ) return false;
Packit dd8086
Packit dd8086
    vendor = ( CFStringRef )
Packit dd8086
      CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) );
Packit dd8086
Packit dd8086
    if ( CFStringGetCString( vendor,
Packit dd8086
                             (char *) &(hw_info->psz_vendor),
Packit dd8086
                             sizeof(hw_info->psz_vendor),
Packit dd8086
                             kCFStringEncodingASCII ) )
Packit dd8086
      CFRelease( vendor );
Packit dd8086
Packit dd8086
    product = ( CFStringRef )
Packit dd8086
      CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) );
Packit dd8086
Packit dd8086
    if ( CFStringGetCString( product,
Packit dd8086
                             (char *) &(hw_info->psz_model),
Packit dd8086
                             sizeof(hw_info->psz_model),
Packit dd8086
                             kCFStringEncodingASCII ) )
Packit dd8086
      CFRelease( product );
Packit dd8086
Packit dd8086
    revision = ( CFStringRef )
Packit dd8086
      CFDictionaryGetValue ( deviceDict,
Packit dd8086
                             CFSTR ( kIOPropertyProductRevisionLevelKey ) );
Packit dd8086
Packit dd8086
    if ( CFStringGetCString( revision,
Packit dd8086
                             (char *) &(hw_info->psz_revision),
Packit dd8086
                             sizeof(hw_info->psz_revision),
Packit dd8086
                             kCFStringEncodingASCII ) )
Packit dd8086
      CFRelease( revision );
Packit dd8086
  }
Packit dd8086
  return true;
Packit dd8086
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
static void
Packit dd8086
_free_osx (void *p_user_data) {
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  if (NULL == p_env) return;
Packit dd8086
  if (p_env->gen.fd != -1)
Packit dd8086
    close(p_env->gen.fd);
Packit dd8086
  if (p_env->MediaClass_service)
Packit dd8086
    IOObjectRelease( p_env->MediaClass_service );
Packit dd8086
  cdio_generic_free(p_env);
Packit dd8086
  if (NULL != p_env->pp_lba)  free((void *) p_env->pp_lba);
Packit dd8086
  if (NULL != p_env->pTOC)    free((void *) p_env->pTOC);
Packit dd8086
Packit dd8086
  if (p_env->scsi_task)
Packit dd8086
    (*p_env->scsi_task)->Release(p_env->scsi_task);
Packit dd8086
Packit dd8086
  if (p_env->pp_scsiTaskDeviceInterface)
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface) ->
Packit dd8086
      ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
Packit dd8086
  if (p_env->pp_scsiTaskDeviceInterface)
Packit dd8086
    (*p_env->pp_scsiTaskDeviceInterface) ->
Packit dd8086
      Release ( p_env->pp_scsiTaskDeviceInterface );
Packit dd8086
Packit dd8086
  if (p_env->mmc)
Packit dd8086
    (*p_env->mmc)->Release(p_env->mmc);
Packit dd8086
Packit dd8086
  if (p_env->plugin)
Packit dd8086
    IODestroyPlugInInterface(p_env->plugin);
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads i_blocks of data sectors from cd device into p_data starting
Packit dd8086
   from i_lsn.
Packit dd8086
   Returns DRIVER_OP_SUCCESS if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_data_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
                       uint16_t i_blocksize, uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_user_data) return DRIVER_OP_UNINIT;
Packit dd8086
Packit dd8086
  {
Packit dd8086
    dk_cd_read_t cd_read;
Packit dd8086
    track_t i_track = cdio_get_track(p_env->gen.cdio, i_lsn);
Packit dd8086
Packit dd8086
    memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
    cd_read.sectorArea  = kCDSectorAreaUser;
Packit dd8086
    cd_read.buffer      = p_data;
Packit dd8086
Packit dd8086
    /* FIXME: Do I have to put use get_track_green_osx? */
Packit dd8086
    switch(get_track_format_osx(p_user_data, i_track)) {
Packit dd8086
    case TRACK_FORMAT_CDI:
Packit dd8086
    case TRACK_FORMAT_DATA:
Packit dd8086
      cd_read.sectorType  = kCDSectorTypeMode1;
Packit dd8086
      cd_read.offset      = i_lsn * kCDSectorSizeMode1;
Packit dd8086
      break;
Packit dd8086
    case TRACK_FORMAT_XA:
Packit dd8086
      cd_read.sectorType  = kCDSectorTypeMode2;
Packit dd8086
      cd_read.offset      = i_lsn * kCDSectorSizeMode2;
Packit dd8086
      break;
Packit dd8086
    default:
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    cd_read.bufferLength = i_blocksize * i_blocks;
Packit dd8086
Packit dd8086
    if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
Packit dd8086
      {
Packit dd8086
        cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
Packit dd8086
        return DRIVER_OP_ERROR;
Packit dd8086
      }
Packit dd8086
    return DRIVER_OP_SUCCESS;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads i_blocks of mode2 form2 sectors from cd device into data starting
Packit dd8086
   from i_lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode1_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
                        bool b_form2, uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  dk_cd_read_t cd_read;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  cd_read.sectorArea  = kCDSectorAreaUser;
Packit dd8086
  cd_read.buffer      = p_data;
Packit dd8086
  cd_read.sectorType  = kCDSectorTypeMode1;
Packit dd8086
Packit dd8086
  if (b_form2) {
Packit dd8086
    cd_read.offset       = i_lsn * kCDSectorSizeMode2;
Packit dd8086
    cd_read.bufferLength = kCDSectorSizeMode2 * i_blocks;
Packit dd8086
  } else {
Packit dd8086
    cd_read.offset       = i_lsn * kCDSectorSizeMode1;
Packit dd8086
    cd_read.bufferLength = kCDSectorSizeMode1 * i_blocks;
Packit dd8086
  }
Packit dd8086
Packit dd8086
   if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
Packit dd8086
  {
Packit dd8086
    cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads i_blocks of mode2 form2 sectors from cd device into data starting
Packit dd8086
   from lsn.
Packit dd8086
   Returns DRIVER_OP_SUCCESS if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode2_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
                        bool b_form2, uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  dk_cd_read_t cd_read;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  cd_read.sectorArea = kCDSectorAreaUser;
Packit dd8086
  cd_read.buffer = p_data;
Packit dd8086
Packit dd8086
  if (b_form2) {
Packit dd8086
    cd_read.offset       = i_lsn * kCDSectorSizeMode2Form2;
Packit dd8086
    cd_read.sectorType   = kCDSectorTypeMode2Form2;
Packit dd8086
    cd_read.bufferLength = kCDSectorSizeMode2Form2 * i_blocks;
Packit dd8086
  } else {
Packit dd8086
    cd_read.offset       = i_lsn * kCDSectorSizeMode2Form1;
Packit dd8086
    cd_read.sectorType   = kCDSectorTypeMode2Form1;
Packit dd8086
    cd_read.bufferLength = kCDSectorSizeMode2Form1 * i_blocks;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
Packit dd8086
  {
Packit dd8086
    cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads a single audio sector from CD device into p_data starting from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static int
Packit dd8086
read_audio_sectors_osx (void *user_data, void *p_data, lsn_t lsn,
Packit dd8086
                             unsigned int i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  dk_cd_read_t cd_read;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  cd_read.offset       = lsn * kCDSectorSizeCDDA;
Packit dd8086
  cd_read.sectorArea   = kCDSectorAreaUser;
Packit dd8086
  cd_read.sectorType   = kCDSectorTypeCDDA;
Packit dd8086
Packit dd8086
  cd_read.buffer       = p_data;
Packit dd8086
  cd_read.bufferLength = kCDSectorSizeCDDA * i_blocks;
Packit dd8086
Packit dd8086
  if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
Packit dd8086
  {
Packit dd8086
    cdio_info( "could not read block %d\n%s", lsn,
Packit dd8086
               strerror(errno));
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads a single mode2 sector from cd device into p_data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode1_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
                       bool b_form2)
Packit dd8086
{
Packit dd8086
  return read_mode1_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Reads a single mode2 sector from cd device into p_data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode2_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
                       bool b_form2)
Packit dd8086
{
Packit dd8086
  return read_mode2_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Set the key "arg" to "value" in source device.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
_set_arg_osx (void *p_user_data, const char key[], const char value[])
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!strcmp (key, "source"))
Packit dd8086
    {
Packit dd8086
      if (!value) return DRIVER_OP_ERROR;
Packit dd8086
      free (p_env->gen.source_name);
Packit dd8086
      p_env->gen.source_name = strdup (value);
Packit dd8086
    }
Packit dd8086
  else if (!strcmp (key, "access-mode"))
Packit dd8086
    {
Packit dd8086
      if (!strcmp(value, "OSX"))
Packit dd8086
        p_env->access_mode = _AM_OSX;
Packit dd8086
      else
Packit dd8086
        cdio_warn ("unknown access type: %s. ignored.", value);
Packit dd8086
    }
Packit dd8086
  else return DRIVER_OP_ERROR;
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
#if 0
Packit dd8086
static void
Packit dd8086
TestDevice(_img_private_t *p_env, io_service_t service)
Packit dd8086
{
Packit dd8086
  SInt32                          score;
Packit dd8086
  HRESULT                         herr;
Packit dd8086
  kern_return_t                   err;
Packit dd8086
  IOCFPlugInInterface             **plugInInterface = NULL;
Packit dd8086
  MMCDeviceInterface              **mmcInterface = NULL;
Packit dd8086
Packit dd8086
  /* Create the IOCFPlugIn interface so we can query it. */
Packit dd8086
Packit dd8086
  err = IOCreatePlugInInterfaceForService ( service,
Packit dd8086
                                            kIOMMCDeviceUserClientTypeID,
Packit dd8086
                                            kIOCFPlugInInterfaceID,
Packit dd8086
                                            &plugInInterface,
Packit dd8086
                                            &score );
Packit dd8086
  if ( err != noErr ) {
Packit dd8086
    printf("IOCreatePlugInInterfaceForService returned %d\n", err);
Packit dd8086
    return;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Query the interface for the MMCDeviceInterface. */
Packit dd8086
Packit dd8086
  herr = ( *plugInInterface )->QueryInterface ( plugInInterface,
Packit dd8086
                                                CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ),
Packit dd8086
                                                ( LPVOID ) &mmcInterface );
Packit dd8086
Packit dd8086
  if ( herr != S_OK )     {
Packit dd8086
    printf("QueryInterface returned %ld\n", herr);
Packit dd8086
    return;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->pp_scsiTaskDeviceInterface =
Packit dd8086
    ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface );
Packit dd8086
Packit dd8086
  if ( NULL == p_env->pp_scsiTaskDeviceInterface )  {
Packit dd8086
    printf("GetSCSITaskDeviceInterface returned NULL\n");
Packit dd8086
    return;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ( *mmcInterface )->Release ( mmcInterface );
Packit dd8086
  IODestroyPlugInInterface ( plugInInterface );
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Read and cache the CD's Track Table of Contents and track info.
Packit dd8086
  Return false if successful or true if an error.
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
read_toc_osx (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  CFDictionaryRef propertiesDict = 0;
Packit dd8086
  CFDataRef data;
Packit dd8086
Packit dd8086
  /* create a CF dictionary containing the TOC */
Packit dd8086
  propertiesDict = GetRegistryEntryProperties( p_env->MediaClass_service );
Packit dd8086
Packit dd8086
  if ( 0 == propertiesDict )     {
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* get the TOC from the dictionary */
Packit dd8086
  data = (CFDataRef) CFDictionaryGetValue( propertiesDict,
Packit dd8086
                                           CFSTR(kIOCDMediaTOCKey) );
Packit dd8086
  if ( data  != NULL ) {
Packit dd8086
    CFRange range;
Packit dd8086
    CFIndex buf_len;
Packit dd8086
Packit dd8086
    buf_len = CFDataGetLength( data ) + 1;
Packit dd8086
    range = CFRangeMake( 0, buf_len );
Packit dd8086
Packit dd8086
    if( ( p_env->pTOC = (CDTOC *)malloc( buf_len ) ) != NULL ) {
Packit dd8086
      CFDataGetBytes( data, range, (u_char *) p_env->pTOC );
Packit dd8086
    } else {
Packit dd8086
      cdio_warn( "Trouble allocating CDROM TOC" );
Packit dd8086
      CFRelease( propertiesDict );
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
  } else     {
Packit dd8086
    cdio_warn( "Trouble reading TOC" );
Packit dd8086
    CFRelease( propertiesDict );
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* TestDevice(p_env, service); */
Packit dd8086
  CFRelease( propertiesDict );
Packit dd8086
Packit dd8086
  p_env->i_descriptors = CDTOCGetDescriptorCount ( p_env->pTOC );
Packit dd8086
Packit dd8086
  /* Read in starting sectors. There may be non-tracks mixed in with
Packit dd8086
     the real tracks.  So find the first and last track number by
Packit dd8086
     scanning. Also find the lead-out track position.
Packit dd8086
   */
Packit dd8086
  {
Packit dd8086
    int i, i_leadout = -1;
Packit dd8086
Packit dd8086
    CDTOCDescriptor *pTrackDescriptors;
Packit dd8086
Packit dd8086
    p_env->pp_lba = malloc( p_env->i_descriptors * sizeof(int) );
Packit dd8086
    if( p_env->pp_lba == NULL )
Packit dd8086
      {
Packit dd8086
        cdio_warn("Out of memory in allocating track starting LSNs" );
Packit dd8086
        free( p_env->pTOC );
Packit dd8086
        return false;
Packit dd8086
      }
Packit dd8086
Packit dd8086
    pTrackDescriptors = p_env->pTOC->descriptors;
Packit dd8086
Packit dd8086
    p_env->gen.i_first_track = CDIO_CD_MAX_TRACKS+1;
Packit dd8086
    p_env->i_last_track      = CDIO_CD_MIN_TRACK_NO;
Packit dd8086
    p_env->i_first_session   = CDIO_CD_MAX_TRACKS+1;
Packit dd8086
    p_env->i_last_session    = CDIO_CD_MIN_TRACK_NO;
Packit dd8086
Packit dd8086
    for( i = 0; i < p_env->i_descriptors; i++ )
Packit dd8086
      {
Packit dd8086
        track_t i_track     = pTrackDescriptors[i].point;
Packit dd8086
        session_t i_session = pTrackDescriptors[i].session;
Packit dd8086
Packit dd8086
        cdio_debug( "point: %d, tno: %d, session: %d, adr: %d, control:%d, "
Packit dd8086
                    "address: %d:%d:%d, p: %d:%d:%d",
Packit dd8086
                    i_track,
Packit dd8086
                    pTrackDescriptors[i].tno, i_session,
Packit dd8086
                    pTrackDescriptors[i].adr, pTrackDescriptors[i].control,
Packit dd8086
                    pTrackDescriptors[i].address.minute,
Packit dd8086
                    pTrackDescriptors[i].address.second,
Packit dd8086
                    pTrackDescriptors[i].address.frame,
Packit dd8086
                    pTrackDescriptors[i].p.minute,
Packit dd8086
                    pTrackDescriptors[i].p.second,
Packit dd8086
                    pTrackDescriptors[i].p.frame );
Packit dd8086
Packit dd8086
        /* track information has adr = 1 */
Packit dd8086
        if ( 0x01 != pTrackDescriptors[i].adr )
Packit dd8086
          continue;
Packit dd8086
Packit dd8086
        if( i_track == OSX_CDROM_LEADOUT_TRACK )
Packit dd8086
          i_leadout = i;
Packit dd8086
Packit dd8086
        if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
Packit dd8086
          continue;
Packit dd8086
Packit dd8086
        if (p_env->gen.i_first_track > i_track)
Packit dd8086
          p_env->gen.i_first_track = i_track;
Packit dd8086
Packit dd8086
        if (p_env->i_last_track < i_track)
Packit dd8086
          p_env->i_last_track = i_track;
Packit dd8086
Packit dd8086
        if (p_env->i_first_session > i_session)
Packit dd8086
          p_env->i_first_session = i_session;
Packit dd8086
Packit dd8086
        if (p_env->i_last_session < i_session)
Packit dd8086
          p_env->i_last_session = i_session;
Packit dd8086
      }
Packit dd8086
Packit dd8086
    /* Now that we know what the first track number is, we can make sure
Packit dd8086
       index positions are ordered starting at 0.
Packit dd8086
     */
Packit dd8086
    for( i = 0; i < p_env->i_descriptors; i++ )
Packit dd8086
      {
Packit dd8086
        track_t i_track = pTrackDescriptors[i].point;
Packit dd8086
Packit dd8086
        if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
Packit dd8086
          continue;
Packit dd8086
Packit dd8086
        /* Note what OSX calls a LBA we call an LSN. So below re we
Packit dd8086
           really have have MSF -> LSN -> LBA.
Packit dd8086
         */
Packit dd8086
        p_env->pp_lba[i_track - p_env->gen.i_first_track] =
Packit dd8086
          cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i].p ));
Packit dd8086
        set_track_flags(&(p_env->gen.track_flags[i_track]),
Packit dd8086
                        pTrackDescriptors[i].control);
Packit dd8086
      }
Packit dd8086
Packit dd8086
    if( i_leadout == -1 )
Packit dd8086
      {
Packit dd8086
        cdio_warn( "CD leadout not found" );
Packit dd8086
        free( p_env->pp_lba );
Packit dd8086
        free( (void *) p_env->pTOC );
Packit dd8086
        return false;
Packit dd8086
      }
Packit dd8086
Packit dd8086
    /* Set leadout sector.
Packit dd8086
       Note what OSX calls a LBA we call an LSN. So below re we
Packit dd8086
       really have have MSF -> LSN -> LBA.
Packit dd8086
    */
Packit dd8086
    p_env->pp_lba[TOTAL_TRACKS] =
Packit dd8086
      cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p ));
Packit dd8086
    p_env->gen.i_tracks = TOTAL_TRACKS;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->gen.toc_init   = true;
Packit dd8086
Packit dd8086
  return( true );
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the starting LSN track number
Packit dd8086
  i_track in obj.  Track numbers start at 1.
Packit dd8086
  The "leadout" track is specified either by
Packit dd8086
  using i_track LEADOUT_TRACK or the total tracks+1.
Packit dd8086
  False is returned if there is no track entry.
Packit dd8086
*/
Packit dd8086
static lsn_t
Packit dd8086
get_track_lba_osx(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
Packit dd8086
  if (!p_env->gen.toc_init) return CDIO_INVALID_LSN;
Packit dd8086
Packit dd8086
  if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->i_last_track+1;
Packit dd8086
Packit dd8086
  if (i_track > p_env->i_last_track + 1 || i_track < p_env->gen.i_first_track) {
Packit dd8086
    return CDIO_INVALID_LSN;
Packit dd8086
  } else {
Packit dd8086
    return p_env->pp_lba[i_track - p_env->gen.i_first_track];
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Eject media . Return DRIVER_OP_SUCCESS if successful.
Packit dd8086
Packit dd8086
  The only way to cleanly unmount the disc under MacOS X (before
Packit dd8086
  Tiger) is to use the 'disktool' command line utility. It uses the
Packit dd8086
  non-public DiskArbitration API, which can not be used by Cocoa or
Packit dd8086
  Carbon applications.
Packit dd8086
Packit dd8086
  Since Tiger (MacOS X 10.4), DiskArbitration is a public framework
Packit dd8086
  and we can use it as needed.
Packit dd8086
Packit dd8086
 */
Packit dd8086
Packit dd8086
#ifndef HAVE_DISKARBITRATION
Packit dd8086
static driver_return_code_t
Packit dd8086
_eject_media_osx (void *user_data) {
Packit dd8086
Packit dd8086
  _img_private_t *p_env = user_data;
Packit dd8086
Packit dd8086
  FILE *p_file;
Packit dd8086
  char *psz_drive;
Packit dd8086
  char sz_cmd[32];
Packit dd8086
Packit dd8086
  if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) != NULL &&
Packit dd8086
      strlen( psz_drive ) > 4 )
Packit dd8086
    {
Packit dd8086
#define EJECT_CMD "/usr/sbin/hdiutil eject %s"
Packit dd8086
      snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_drive );
Packit dd8086
#undef EJECT_CMD
Packit dd8086
Packit dd8086
      if( ( p_file = popen( sz_cmd, "r" ) ) != NULL )
Packit dd8086
        {
Packit dd8086
          char psz_result[0x200];
Packit dd8086
          int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file );
Packit dd8086
Packit dd8086
          if( i_ret == 0 && ferror( p_file ) != 0 )
Packit dd8086
            {
Packit dd8086
              pclose( p_file );
Packit dd8086
              return DRIVER_OP_ERROR;
Packit dd8086
            }
Packit dd8086
Packit dd8086
          pclose( p_file );
Packit dd8086
Packit dd8086
          psz_result[ i_ret ] = 0;
Packit dd8086
Packit dd8086
          if( strstr( psz_result, "Disk Ejected" ) != NULL )
Packit dd8086
            {
Packit dd8086
              return DRIVER_OP_SUCCESS;
Packit dd8086
            }
Packit dd8086
        }
Packit dd8086
    }
Packit dd8086
Packit dd8086
  return DRIVER_OP_ERROR;
Packit dd8086
}
Packit dd8086
#else /* HAVE_DISKARBITRATION */
Packit dd8086
typedef struct dacontext_s {
Packit dd8086
    int                 result;
Packit dd8086
    Boolean             completed;
Packit dd8086
    DASessionRef        session;
Packit dd8086
    CFRunLoopRef        runloop;
Packit dd8086
    CFRunLoopSourceRef  cancel;
Packit dd8086
} dacontext_t;
Packit dd8086
Packit dd8086
static void cancel_runloop(void *info) { /* do nothing */ }
Packit dd8086
Packit dd8086
static CFRunLoopSourceContext cancelRunLoopSourceContext = {
Packit dd8086
    .perform = cancel_runloop
Packit dd8086
};
Packit dd8086
Packit dd8086
static void media_eject_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
Packit dd8086
{
Packit dd8086
    dacontext_t *dacontext = (dacontext_t *)context;
Packit dd8086
Packit dd8086
    if ( dissenter )
Packit dd8086
      {
Packit dd8086
        CFStringRef status = DADissenterGetStatusString(dissenter);
Packit dd8086
        if (status)
Packit dd8086
        {
Packit dd8086
                size_t cstr_size = CFStringGetLength(status);
Packit dd8086
                char *cstr = malloc(cstr_size);
Packit dd8086
                if ( CFStringGetCString( status,
Packit dd8086
                                 cstr, cstr_size,
Packit dd8086
                                 kCFStringEncodingASCII ) )
Packit dd8086
                CFRelease( status );
Packit dd8086
Packit dd8086
                cdio_warn("%s", cstr);
Packit dd8086
Packit dd8086
                free(cstr);
Packit dd8086
        }
Packit dd8086
      }
Packit dd8086
Packit dd8086
    dacontext->result    = (dissenter ? DRIVER_OP_ERROR : DRIVER_OP_SUCCESS);
Packit dd8086
    dacontext->completed = TRUE;
Packit dd8086
    CFRunLoopSourceSignal(dacontext->cancel);
Packit dd8086
    CFRunLoopWakeUp(dacontext->runloop);
Packit dd8086
}
Packit dd8086
Packit dd8086
static void media_unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
Packit dd8086
{
Packit dd8086
    dacontext_t *dacontext = (dacontext_t *)context;
Packit dd8086
Packit dd8086
    if (!dissenter) {
Packit dd8086
        DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, context);
Packit dd8086
        dacontext->result = dacontext->result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext->result;
Packit dd8086
    }
Packit dd8086
    else {
Packit dd8086
        dacontext->result    = DRIVER_OP_ERROR;
Packit dd8086
        dacontext->completed = TRUE;
Packit dd8086
        CFRunLoopSourceSignal(dacontext->cancel);
Packit dd8086
        CFRunLoopWakeUp(dacontext->runloop);
Packit dd8086
    }
Packit dd8086
}
Packit dd8086
Packit dd8086
static driver_return_code_t
Packit dd8086
_eject_media_osx (void *user_data) {
Packit dd8086
Packit dd8086
  _img_private_t *p_env = user_data;
Packit dd8086
  char *psz_drive;
Packit dd8086
Packit dd8086
  DADiskRef       disk;
Packit dd8086
  dacontext_t     dacontext;
Packit dd8086
  CFDictionaryRef description;
Packit dd8086
Packit dd8086
  if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) == NULL ||
Packit dd8086
      strlen( psz_drive ) <= 4 )
Packit dd8086
    {
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  if (p_env->gen.fd != -1)
Packit dd8086
    close(p_env->gen.fd);
Packit dd8086
  p_env->gen.fd = -1;
Packit dd8086
Packit dd8086
  dacontext.result    = DRIVER_OP_UNINIT;
Packit dd8086
  dacontext.completed = FALSE;
Packit dd8086
  dacontext.runloop   = CFRunLoopGetCurrent();
Packit dd8086
  dacontext.cancel    = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cancelRunLoopSourceContext);
Packit dd8086
Packit dd8086
  if (!dacontext.cancel)
Packit dd8086
    {
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  if (!(dacontext.session = DASessionCreate(kCFAllocatorDefault)))
Packit dd8086
    {
Packit dd8086
      CFRelease(dacontext.cancel);
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  if ((disk = DADiskCreateFromBSDName(kCFAllocatorDefault, dacontext.session, psz_drive)) != NULL)
Packit dd8086
    {
Packit dd8086
      if ((description = DADiskCopyDescription(disk)) != NULL)
Packit dd8086
        {
Packit dd8086
          /* Does the device need to be unmounted first? */
Packit dd8086
          DASessionScheduleWithRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode);
Packit dd8086
          CFRunLoopAddSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode);
Packit dd8086
Packit dd8086
          if (CFDictionaryGetValueIfPresent(description, kDADiskDescriptionVolumePathKey, NULL))
Packit dd8086
            {
Packit dd8086
              DADiskUnmount(disk, kDADiskUnmountOptionWhole, media_unmount_callback, &dacontext);
Packit dd8086
            }
Packit dd8086
          else
Packit dd8086
            {
Packit dd8086
              DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, &dacontext);
Packit dd8086
              dacontext.result = dacontext.result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext.result;
Packit dd8086
            }
Packit dd8086
          while (!dacontext.completed)
Packit dd8086
            {
Packit dd8086
              if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 30.0, TRUE) == kCFRunLoopRunTimedOut) break;  /* timeout after 30 seconds */
Packit dd8086
            }
Packit dd8086
          CFRunLoopRemoveSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode);
Packit dd8086
          DASessionUnscheduleFromRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode);
Packit dd8086
          CFRelease(description);
Packit dd8086
        }
Packit dd8086
      CFRelease(disk);
Packit dd8086
    }
Packit dd8086
Packit dd8086
  CFRunLoopSourceInvalidate(dacontext.cancel);
Packit dd8086
  CFRelease(dacontext.cancel);
Packit dd8086
  CFRelease(dacontext.session);
Packit dd8086
  return dacontext.result;
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Return the size of the CD in logical block address (LBA) units.
Packit dd8086
 */
Packit dd8086
static lsn_t
Packit dd8086
get_disc_last_lsn_osx (void *user_data)
Packit dd8086
{
Packit dd8086
  return get_track_lba_osx(user_data, CDIO_CDROM_LEADOUT_TRACK);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the value associated with the key "arg".
Packit dd8086
*/
Packit dd8086
static const char *
Packit dd8086
_get_arg_osx (void *user_data, const char key[])
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = user_data;
Packit dd8086
Packit dd8086
  if (!strcmp (key, "source")) {
Packit dd8086
    return p_env->gen.source_name;
Packit dd8086
  } else if (!strcmp (key, "access-mode")) {
Packit dd8086
    switch (p_env->access_mode) {
Packit dd8086
    case _AM_OSX:
Packit dd8086
      return "OS X";
Packit dd8086
    case _AM_NONE:
Packit dd8086
      return "no access method";
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the media catalog number MCN.
Packit dd8086
 */
Packit dd8086
static char *
Packit dd8086
get_mcn_osx (const void *user_data) {
Packit dd8086
  const _img_private_t *p_env = user_data;
Packit dd8086
  dk_cd_read_mcn_t cd_read;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  if( ioctl( p_env->gen.fd, DKIOCCDREADMCN, &cd_read ) < 0 )
Packit dd8086
  {
Packit dd8086
    cdio_debug( "could not read MCN, %s", strerror(errno) );
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
  return strdup((char*)cd_read.mcn);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the international standard recording code ISRC.
Packit dd8086
 */
Packit dd8086
static char *
Packit dd8086
get_track_isrc_osx (const void *user_data, track_t i_track) {
Packit dd8086
  const _img_private_t *p_env = user_data;
Packit dd8086
  dk_cd_read_isrc_t cd_read;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  cd_read.track = i_track;
Packit dd8086
Packit dd8086
  if( ioctl( p_env->gen.fd, DKIOCCDREADISRC, &cd_read ) < 0 )
Packit dd8086
  {
Packit dd8086
    cdio_debug( "could not read ISRC, %s", strerror(errno) );
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
  return strdup((char*)cd_read.isrc);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get format of track.
Packit dd8086
*/
Packit dd8086
static track_format_t
Packit dd8086
get_track_format_osx(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  dk_cd_read_track_info_t cd_read;
Packit dd8086
  CDTrackInfo a_track;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
Packit dd8086
Packit dd8086
  if (i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track)
Packit dd8086
    return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
  cd_read.address = i_track;
Packit dd8086
  cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber;
Packit dd8086
Packit dd8086
  cd_read.buffer = &a_track;
Packit dd8086
  cd_read.bufferLength = sizeof(CDTrackInfo);
Packit dd8086
Packit dd8086
  if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 )
Packit dd8086
  {
Packit dd8086
    cdio_warn( "could not read trackinfo for track %d:\n%s", i_track,
Packit dd8086
               strerror(errno));
Packit dd8086
    return TRACK_FORMAT_ERROR;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  cdio_debug( "%d: trackinfo trackMode: %x dataMode: %x", i_track,
Packit dd8086
              a_track.trackMode, a_track.dataMode );
Packit dd8086
Packit dd8086
  if (a_track.trackMode == CDIO_CDROM_DATA_TRACK) {
Packit dd8086
    if (a_track.dataMode == CDROM_CDI_TRACK) {
Packit dd8086
      return TRACK_FORMAT_CDI;
Packit dd8086
    } else if (a_track.dataMode == CDROM_XA_TRACK) {
Packit dd8086
      return TRACK_FORMAT_XA;
Packit dd8086
    } else {
Packit dd8086
      return TRACK_FORMAT_DATA;
Packit dd8086
    }
Packit dd8086
  } else {
Packit dd8086
    return TRACK_FORMAT_AUDIO;
Packit dd8086
  }
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return true if we have XA data (green, mode2 form1) or
Packit dd8086
  XA data (green, mode2 form2). That is track begins:
Packit dd8086
  sync - header - subheader
Packit dd8086
  12     4      -  8
Packit dd8086
Packit dd8086
  FIXME: there's gotta be a better design for this and get_track_format?
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
get_track_green_osx(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  CDTrackInfo a_track;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
Packit dd8086
Packit dd8086
  if ( i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track )
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  else {
Packit dd8086
Packit dd8086
    dk_cd_read_track_info_t cd_read;
Packit dd8086
Packit dd8086
    memset( &cd_read, 0, sizeof(cd_read) );
Packit dd8086
Packit dd8086
    cd_read.address      = i_track;
Packit dd8086
    cd_read.addressType  = kCDTrackInfoAddressTypeTrackNumber;
Packit dd8086
Packit dd8086
    cd_read.buffer       = &a_track;
Packit dd8086
    cd_read.bufferLength = sizeof(CDTrackInfo);
Packit dd8086
Packit dd8086
    if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) {
Packit dd8086
      cdio_warn( "could not read trackinfo for track %d:\n%s", i_track,
Packit dd8086
                 strerror(errno));
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
    return ((a_track.trackMode & CDIO_CDROM_DATA_TRACK) != 0);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Set CD-ROM drive speed */
Packit dd8086
static int
Packit dd8086
set_speed_osx (void *p_user_data, int i_speed)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env) return -1;
Packit dd8086
  return ioctl(p_env->gen.fd, DKIOCCDSETSPEED, i_speed);
Packit dd8086
}
Packit dd8086
Packit dd8086
#endif /* HAVE_DARWIN_CDROM */
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Close tray on CD-ROM.
Packit dd8086
Packit dd8086
  @param psz_drive the CD-ROM drive to be closed.
Packit dd8086
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* FIXME: We don't use the device name because we don't how
Packit dd8086
   to.
Packit dd8086
 */
Packit dd8086
#define CLOSE_TRAY_CMD "/usr/sbin/drutil tray close"
Packit dd8086
driver_return_code_t
Packit dd8086
close_tray_osx (const char *psz_drive)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_DARWIN_CDROM
Packit dd8086
  FILE *p_file;
Packit dd8086
  char sz_cmd[80];
Packit dd8086
Packit dd8086
  if ( !psz_drive) return DRIVER_OP_UNINIT;
Packit dd8086
Packit dd8086
  /* Right now we really aren't making use of snprintf, but
Packit dd8086
     possibly someday we will.
Packit dd8086
   */
Packit dd8086
  snprintf( sz_cmd, sizeof(sz_cmd), CLOSE_TRAY_CMD );
Packit dd8086
Packit dd8086
  if( ( p_file = popen( sz_cmd, "r" ) ) != NULL )
Packit dd8086
    {
Packit dd8086
      char psz_result[0x200];
Packit dd8086
      int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file );
Packit dd8086
Packit dd8086
      if( i_ret == 0 && ferror( p_file ) != 0 )
Packit dd8086
        {
Packit dd8086
          pclose( p_file );
Packit dd8086
          return DRIVER_OP_ERROR;
Packit dd8086
        }
Packit dd8086
Packit dd8086
      pclose( p_file );
Packit dd8086
Packit dd8086
      psz_result[ i_ret ] = 0;
Packit dd8086
Packit dd8086
      if( 0 == i_ret )
Packit dd8086
        {
Packit dd8086
          return DRIVER_OP_SUCCESS;
Packit dd8086
        }
Packit dd8086
    }
Packit dd8086
Packit dd8086
  return DRIVER_OP_ERROR;
Packit dd8086
#else
Packit dd8086
  return DRIVER_OP_NO_DRIVER;
Packit dd8086
#endif /*HAVE_DARWIN_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return a string containing the default CD device if none is specified.
Packit dd8086
 */
Packit dd8086
char **
Packit dd8086
cdio_get_devices_osx(void)
Packit dd8086
{
Packit dd8086
#ifndef HAVE_DARWIN_CDROM
Packit dd8086
  return NULL;
Packit dd8086
#else
Packit dd8086
  io_object_t   next_media;
Packit dd8086
  mach_port_t   master_port;
Packit dd8086
  kern_return_t kern_result;
Packit dd8086
  io_iterator_t media_iterator;
Packit dd8086
  CFMutableDictionaryRef classes_to_match;
Packit dd8086
  char        **drives = NULL;
Packit dd8086
  unsigned int  num_drives=0;
Packit dd8086
Packit dd8086
  /* Probe devices to get up to date information. */
Packit dd8086
  ProbeStorageDevices();
Packit dd8086
Packit dd8086
  kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
Packit dd8086
  if( kern_result != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
      return( NULL );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  classes_to_match = IOServiceMatching( kIOMediaClass );
Packit dd8086
  if( classes_to_match == NULL )
Packit dd8086
    {
Packit dd8086
      return( NULL );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
Packit dd8086
                        kCFBooleanTrue );
Packit dd8086
Packit dd8086
  CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaWholeKey),
Packit dd8086
                        kCFBooleanTrue );
Packit dd8086
Packit dd8086
  kern_result = IOServiceGetMatchingServices( master_port,
Packit dd8086
                                              classes_to_match,
Packit dd8086
                                              &media_iterator );
Packit dd8086
  if( kern_result != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
      return( NULL );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  next_media = IOIteratorNext( media_iterator );
Packit dd8086
  if( next_media != 0 )
Packit dd8086
    {
Packit dd8086
      char psz_buf[0x32];
Packit dd8086
      size_t dev_path_length;
Packit dd8086
      CFTypeRef str_bsd_path;
Packit dd8086
Packit dd8086
      do
Packit dd8086
        {
Packit dd8086
          str_bsd_path =
Packit dd8086
            IORegistryEntryCreateCFProperty( next_media,
Packit dd8086
                                             CFSTR( kIOBSDNameKey ),
Packit dd8086
                                             kCFAllocatorDefault,
Packit dd8086
                                             0 );
Packit dd8086
          if( str_bsd_path == NULL )
Packit dd8086
            {
Packit dd8086
              IOObjectRelease( next_media );
Packit dd8086
              continue;
Packit dd8086
            }
Packit dd8086
Packit dd8086
          /* Below, by appending 'r' to the BSD node name, we indicate
Packit dd8086
             a raw disk. Raw disks receive I/O requests directly and
Packit dd8086
             don't go through a buffer cache. */
Packit dd8086
          snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
Packit dd8086
          dev_path_length = strlen( psz_buf );
Packit dd8086
Packit dd8086
          if( CFStringGetCString( str_bsd_path,
Packit dd8086
                                  (char*)&psz_buf + dev_path_length,
Packit dd8086
                                  sizeof(psz_buf) - dev_path_length,
Packit dd8086
                                  kCFStringEncodingASCII ) )
Packit dd8086
            {
Packit dd8086
              cdio_add_device_list(&drives, strdup(psz_buf), &num_drives);
Packit dd8086
            }
Packit dd8086
          CFRelease( str_bsd_path );
Packit dd8086
          IOObjectRelease( next_media );
Packit dd8086
Packit dd8086
        } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
Packit dd8086
    }
Packit dd8086
  IOObjectRelease( media_iterator );
Packit dd8086
  cdio_add_device_list(&drives, NULL, &num_drives);
Packit dd8086
  return drives;
Packit dd8086
#endif /* HAVE_DARWIN_CDROM */
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return a string containing the default CD device if none is specified.
Packit dd8086
 */
Packit dd8086
char *
Packit dd8086
cdio_get_default_device_osx(void)
Packit dd8086
{
Packit dd8086
#ifndef HAVE_DARWIN_CDROM
Packit dd8086
  return NULL;
Packit dd8086
#else
Packit dd8086
  io_object_t   next_media;
Packit dd8086
  kern_return_t kern_result;
Packit dd8086
  io_iterator_t media_iterator;
Packit dd8086
  CFMutableDictionaryRef classes_to_match;
Packit dd8086
Packit dd8086
  /* Probe devices to get up to date information. */
Packit dd8086
  ProbeStorageDevices();
Packit dd8086
Packit dd8086
  classes_to_match = IOServiceMatching( kIOMediaClass );
Packit dd8086
  if( classes_to_match == NULL )
Packit dd8086
    {
Packit dd8086
      return( NULL );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
Packit dd8086
                        kCFBooleanTrue );
Packit dd8086
Packit dd8086
  CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaWholeKey),
Packit dd8086
                        kCFBooleanTrue );
Packit dd8086
Packit dd8086
  kern_result = IOServiceGetMatchingServices( kIOMasterPortDefault,
Packit dd8086
                                              classes_to_match,
Packit dd8086
                                              &media_iterator );
Packit dd8086
  if( kern_result != KERN_SUCCESS )
Packit dd8086
    {
Packit dd8086
      return( NULL );
Packit dd8086
    }
Packit dd8086
Packit dd8086
  next_media = IOIteratorNext( media_iterator );
Packit dd8086
  if( next_media != 0 )
Packit dd8086
    {
Packit dd8086
      char psz_buf[0x32];
Packit dd8086
      size_t dev_path_length;
Packit dd8086
      CFTypeRef str_bsd_path;
Packit dd8086
Packit dd8086
      do
Packit dd8086
        {
Packit dd8086
          /* Skip other removable media, like USB flash memory keys:  */
Packit dd8086
          if (!IOObjectConformsTo(next_media, kIODVDMediaClass) &&
Packit dd8086
              !IOObjectConformsTo(next_media, kIOCDMediaClass) &&
Packit dd8086
              !IOObjectConformsTo(next_media, kIOBDMediaClass))
Packit dd8086
            continue;
Packit dd8086
Packit dd8086
          str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
Packit dd8086
                                                          CFSTR( kIOBSDNameKey ),
Packit dd8086
                                                          kCFAllocatorDefault,
Packit dd8086
                                                          0 );
Packit dd8086
          if( str_bsd_path == NULL )
Packit dd8086
            {
Packit dd8086
              IOObjectRelease( next_media );
Packit dd8086
              continue;
Packit dd8086
            }
Packit dd8086
Packit dd8086
          snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
Packit dd8086
          dev_path_length = strlen( psz_buf );
Packit dd8086
Packit dd8086
          if( CFStringGetCString( str_bsd_path,
Packit dd8086
                                  (char*)&psz_buf + dev_path_length,
Packit dd8086
                                  sizeof(psz_buf) - dev_path_length,
Packit dd8086
                                  kCFStringEncodingASCII ) )
Packit dd8086
            {
Packit dd8086
              CFRelease( str_bsd_path );
Packit dd8086
              IOObjectRelease( next_media );
Packit dd8086
              IOObjectRelease( media_iterator );
Packit dd8086
              return strdup( psz_buf );
Packit dd8086
            }
Packit dd8086
Packit dd8086
          CFRelease( str_bsd_path );
Packit dd8086
          IOObjectRelease( next_media );
Packit dd8086
Packit dd8086
        } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
Packit dd8086
    }
Packit dd8086
  IOObjectRelease( media_iterator );
Packit dd8086
  cdio_warn ("cdio_get_default_device() - No CD/DVD/BD media - returning NULL");
Packit dd8086
  return NULL;
Packit dd8086
#endif /* HAVE_DARWIN_CDROM */
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_am_osx (const char *psz_source_name, const char *psz_access_mode)
Packit dd8086
{
Packit dd8086
Packit dd8086
  if (psz_access_mode != NULL)
Packit dd8086
    cdio_warn ("there is only one access mode for OS X. Arg %s ignored",
Packit dd8086
               psz_access_mode);
Packit dd8086
  return cdio_open_osx(psz_source_name);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_osx (const char *psz_orig_source)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_DARWIN_CDROM
Packit dd8086
  CdIo_t *ret;
Packit dd8086
  _img_private_t *_data;
Packit dd8086
  char *psz_source;
Packit dd8086
Packit dd8086
  cdio_funcs_t _funcs = {
Packit dd8086
    .eject_media           = _eject_media_osx,
Packit dd8086
    .free                  = _free_osx,
Packit dd8086
    .get_arg               = _get_arg_osx,
Packit dd8086
    .get_cdtext            = get_cdtext_generic,
Packit dd8086
    .get_cdtext_raw        = read_cdtext_generic,
Packit dd8086
    .get_default_device    = cdio_get_default_device_osx,
Packit dd8086
    .get_devices           = cdio_get_devices_osx,
Packit dd8086
    .get_disc_last_lsn     = get_disc_last_lsn_osx,
Packit dd8086
    .get_discmode          = get_discmode_osx,
Packit dd8086
    .get_drive_cap         = get_drive_cap_osx,
Packit dd8086
    .get_first_track_num   = get_first_track_num_generic,
Packit dd8086
    .get_hwinfo            = get_hwinfo_osx,
Packit dd8086
    .get_mcn               = get_mcn_osx,
Packit dd8086
    .get_num_tracks        = get_num_tracks_generic,
Packit dd8086
    .get_track_channels    = get_track_channels_generic,
Packit dd8086
    .get_track_copy_permit = get_track_copy_permit_generic,
Packit dd8086
    .get_track_format      = get_track_format_osx,
Packit dd8086
    .get_track_green       = get_track_green_osx,
Packit dd8086
    .get_track_lba         = get_track_lba_osx,
Packit dd8086
    .get_track_msf         = NULL,
Packit dd8086
    .get_track_preemphasis = get_track_preemphasis_generic,
Packit dd8086
    .get_track_isrc        = get_track_isrc_osx,
Packit dd8086
    .lseek                 = cdio_generic_lseek,
Packit dd8086
    .read                  = cdio_generic_read,
Packit dd8086
    .read_audio_sectors    = read_audio_sectors_osx,
Packit dd8086
    .read_data_sectors     = read_data_sectors_osx,
Packit dd8086
    .read_mode1_sector     = read_mode1_sector_osx,
Packit dd8086
    .read_mode1_sectors    = read_mode1_sectors_osx,
Packit dd8086
    .read_mode2_sector     = read_mode2_sector_osx,
Packit dd8086
    .read_mode2_sectors    = read_mode2_sectors_osx,
Packit dd8086
    .read_toc              = read_toc_osx,
Packit dd8086
    .run_mmc_cmd           = run_mmc_cmd_osx,
Packit dd8086
    .set_arg               = _set_arg_osx,
Packit dd8086
    .set_speed             = set_speed_osx,
Packit dd8086
  };
Packit dd8086
Packit dd8086
  _data                     = calloc (1, sizeof (_img_private_t));
Packit dd8086
  _data->access_mode        = _AM_OSX;
Packit dd8086
  _data->MediaClass_service = 0;
Packit dd8086
  _data->gen.init           = false;
Packit dd8086
  _data->gen.fd             = -1;
Packit dd8086
  _data->gen.toc_init       = false;
Packit dd8086
  _data->gen.b_cdtext_error = false;
Packit dd8086
Packit dd8086
  if (NULL == psz_orig_source) {
Packit dd8086
    psz_source=cdio_get_default_device_osx();
Packit dd8086
    if (NULL == psz_source) {
Packit dd8086
	goto error_exit;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    _set_arg_osx(_data, "source", psz_source);
Packit dd8086
    free(psz_source);
Packit dd8086
  } else {
Packit dd8086
    if (cdio_is_device_generic(psz_orig_source))
Packit dd8086
      _set_arg_osx(_data, "source", psz_orig_source);
Packit dd8086
    else {
Packit dd8086
      /* The below would be okay if all device drivers worked this way. */
Packit dd8086
#if 0
Packit dd8086
      cdio_info ("source %s is a not a device", psz_orig_source);
Packit dd8086
#endif
Packit dd8086
      goto error_exit;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret = cdio_new ((void *)_data, &_funcs);
Packit dd8086
  if (ret == NULL)
Packit dd8086
    goto error_exit;
Packit dd8086
Packit dd8086
  ret->driver_id = DRIVER_OSX;
Packit dd8086
Packit dd8086
  if (cdio_generic_init(_data, O_RDONLY | O_NONBLOCK) && init_osx(_data)) {
Packit dd8086
    return ret;
Packit dd8086
  }
Packit dd8086
  free(ret);
Packit dd8086
 error_exit:
Packit dd8086
  cdio_generic_free(_data);
Packit dd8086
  return NULL;
Packit dd8086
Packit dd8086
#else
Packit dd8086
  return NULL;
Packit dd8086
#endif /* HAVE_DARWIN_CDROM */
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
bool
Packit dd8086
cdio_have_osx (void)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_DARWIN_CDROM
Packit dd8086
  return true;
Packit dd8086
#else
Packit dd8086
  return false;
Packit dd8086
#endif /* HAVE_DARWIN_CDROM */
Packit dd8086
}