📄 _cdio_osx.c
字号:
/* $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"#endifstatic 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 0x2typedef enum { _AM_NONE, _AM_OSX,} access_mode_t;#define MAX_SERVICE_NAME 1000typedef 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 CFMutableDictionaryRefGetRegistryEntryProperties ( 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 intrun_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_tGetDeviceIterator ( 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 boolGetFeaturesFlagsForDrive ( 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_tget_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_tget_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 voidget_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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -