/* * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Includes //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Libkern includes #include // Generic IOKit related headers #include #include // Generic IOKit storage related headers #include // User notification includes #include // SCSI Architecture Model Family includes #include #include #include "IOSCSIProtocolInterface.h" #include "IOSCSIMultimediaCommandsDevice.h" #include "IODVDServices.h" #include "IOCompactDiscServices.h" #include "SCSIBlockCommands.h" #include "SCSIMultimediaCommands.h" // Temporary definition until the actual one is available in IODVDTypes.h #define kDVDMediaTypePlusR 0x0207 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Flag to turn on compiling of APIs marked as obsolete #define INCLUDE_OBSOLETE_APIS 1 #define DEBUG 0 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "MMC" #if DEBUG #define SCSI_MMC_DEVICE_DEBUGGING_LEVEL 0 #endif #include "IOSCSIArchitectureModelFamilyDebugging.h" #if ( SCSI_MMC_DEVICE_DEBUGGING_LEVEL >= 1 ) #define PANIC_NOW(x) IOPanic x #else #define PANIC_NOW(x) #endif #if ( SCSI_MMC_DEVICE_DEBUGGING_LEVEL >= 2 ) #define ERROR_LOG(x) IOLog x #else #define ERROR_LOG(x) #endif #if ( SCSI_MMC_DEVICE_DEBUGGING_LEVEL >= 3 ) #define STATUS_LOG(x) IOLog x #else #define STATUS_LOG(x) #endif #define super IOSCSIPrimaryCommandsDevice OSDefineMetaClass ( IOSCSIMultimediaCommandsDevice, IOSCSIPrimaryCommandsDevice ); OSDefineAbstractStructors ( IOSCSIMultimediaCommandsDevice, IOSCSIPrimaryCommandsDevice ); //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Constants //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ #define kMaxProfileSize 56 #define kDiscInformationSize 32 #define kATIPBufferSize 16 #define kTrackInfoBufferSize 8 #define kDVDPhysicalFormatInfoBufferSize 8 #define kProfileDataLengthFieldSize 4 #define kProfileFeatureHeaderSize 8 #define kProfileDescriptorSize 4 #define kModeSense6ParameterHeaderSize 4 #define kModeSense10ParameterHeaderSize 8 #define kMechanicalCapabilitiesMinBufferSize 4 #define kSubChannelDataBufferSize 24 #define kCDAudioModePageBufferSize 24 // Offsets into the CD Mechanical Capabilities mode page #define kMediaAccessSpeedOffset 14 #define kMaxRetryCount 8 #define kDefaultMaxBlocksPerIO 65535 #define kMechanicalCapabilitiesModePageCode 0x2A #define kCDAudioModePageCode 0x0E #define kAppleKeySwitchProperty "AppleKeyswitch" #define kLocalizationPath "/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/IOSCSIMultimediaCommandsDevice.kext" #define kMediaEjectHeaderString "Media Eject Header" #define kMediaEjectNoticeString "Media Eject Notice" #define kOKButtonString "OK" #define kEmptyString "" // Get Configuration Profiles enum { kGetConfigurationProfileList = 0x0000, /* Profile List */ kGetConfigurationProfileRandomWrite = 0x0020, /* Random Write Profile */ kGetConfigurationProfileIncrementalStreamedWrite = 0x0021, /* Incremental Streamed Writing Profile */ kGetConfigurationProfileDVDPlusRW = 0x002A, /* DVD+RW Profile */ kGetConfigurationProfileDVDPlusR = 0x002B, /* DVD+R Profile */ kGetConfigurationProfileCDTAO = 0x002D, /* CD Track At Once Profile */ kGetConfigurationProfileCDMastering = 0x002E, /* CD Mastering Profile */ kGetConfigurationProfileDVDWrite = 0x002F, /* DVD-R Write Profile */ kGetConfigurationProfileAnalogAudio = 0x0103, /* Analog Audio Profile */ kGetConfigurationProfileDVDCSS = 0x0106 /* DVD-CSS Profile */ }; // Get Configuration Feature Numbers in Profile List enum { kGetConfigurationCDROM_Feature = 0x0008, kGetConfigurationCDR_Feature = 0x0009, kGetConfigurationCDRW_Feature = 0x000A, kGetConfigurationDVDROM_Feature = 0x0010, kGetConfigurationDVDR_Feature = 0x0011, kGetConfigurationDVDRAM_Feature = 0x0012, kGetConfigurationDVDRW_Feature = 0x0014, kGetConfigurationDVDPlusRW_Feature = 0x001A, kGetConfigurationDVDPlusR_Feature = 0x001B }; // Mechanical Capabilities flags enum { kMechanicalCapabilitiesCDRMask = 0x01, kMechanicalCapabilitiesCDRWMask = 0x02, kMechanicalCapabilitiesTestWriteMask = 0x04, kMechanicalCapabilitiesDVDROMMask = 0x08, kMechanicalCapabilitiesDVDRMask = 0x10, kMechanicalCapabilitiesDVDRAMMask = 0x20, }; enum { kMechanicalCapabilitiesAnalogAudioMask = 0x01, kMechanicalCapabilitiesCDDAStreamAccurateMask = 0x02, kMechanicalCapabilitiesBUFMask = 0x80, }; // Random Writable Protection (DVD-RAM DVD+RW protection mask) enum { kRandomWritableProtectionMask = 0x01 }; // DiscType mask enum { kDiscTypeCDRWMask = 0x40 }; // Media Catalog Number and ISRC masks enum { kMediaCatalogValueFieldValidMask = 0x80, kTrackCatalogValueFieldValidMask = 0x80 }; // Spindle speed settings enum { kCDSpeed1x = 176, kDVDSpeed1x = 1350 }; // Apple Features mode page code #define kAppleFeaturesModePageCode 0x31 // Apple Features mode page struct struct AppleFeatures { UInt16 dataLength; // should always be 0x10 UInt8 mediumType; // should always be 0 UInt8 reserved[5]; // reserved, should always be 0 UInt8 pageCode; // should always be kAppleFeaturesModePageCode UInt8 pageLength; // should always be 0x06 UInt8 signature[4]; // should always be '.App' #if defined(__LITTLE_ENDIAN__) UInt8 supportsLowPowerPoll:1; // flag - supports sleep shortcut UInt8 reservedBits:7; // reserved, should always be 0 #else /* ! defined(__LITTLE_ENDIAN__) */ UInt8 reservedBits:7; // reserved, should always be 0 UInt8 supportsLowPowerPoll:1; // flag - supports sleep shortcut #endif /* defined(__LITTLE_ENDIAN__) */ UInt8 reserved2; // reserved, should always be 0 }; typedef struct AppleFeatures AppleFeatures; #if 0 #pragma mark - #pragma mark ₯ Public Methods - API Exported to layers above #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SyncReadWrite - Translates a synchronous I/O request into a // read or a write. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SyncReadWrite ( IOMemoryDescriptor * buffer, UInt64 startBlock, UInt64 blockCount ) { IODirection direction; IOReturn status = kIOReturnBadArgument; require ( IsProtocolAccessEnabled ( ), ErrorExit ); require ( IsDeviceAccessEnabled ( ), ErrorExit ); direction = buffer->getDirection ( ); if ( direction == kIODirectionIn ) { status = IssueRead ( buffer, startBlock, blockCount ); } else if ( direction == kIODirectionOut ) { status = IssueWrite ( buffer, startBlock, blockCount ); } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AsyncReadWrite - Translates an asynchronous I/O request into a // read or a write. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AsyncReadWrite ( IOMemoryDescriptor * buffer, UInt64 startBlock, UInt64 blockCount, void * clientData ) { IODirection direction; IOReturn status = kIOReturnBadArgument; require ( IsProtocolAccessEnabled ( ), ErrorExit ); require ( IsDeviceAccessEnabled ( ), ErrorExit ); direction = buffer->getDirection ( ); if ( direction == kIODirectionIn ) { status = IssueRead ( buffer, clientData, startBlock, blockCount ); } else if ( direction == kIODirectionOut ) { status = IssueWrite ( buffer, clientData, startBlock, blockCount ); } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ EjectTheMedia - Unlocks and ejects the media if it is removable. If it // is not removable, it synchronizes the write cache. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::EjectTheMedia ( void ) { SCSITaskIdentifier request = NULL; IOReturn status = kIOReturnNoResources; STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( fMediaIsRemovable == false ) { if ( SYNCHRONIZE_CACHE ( request, 0, 0, 0, 0, 0 ) == true ) { // The command was successfully built, now send it ( void ) SendCommand ( request, kThirtySecondTimeoutInMS ); } } else { if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateUnlocked, 0 ) == true ) { // The command was successfully built, now send it ( void ) SendCommand ( request, kTenSecondTimeoutInMS ); } if ( START_STOP_UNIT ( request, 0, 0, 1, 0, 0 ) == true ) { // The command was successfully built, now send it ( void ) SendCommand ( request, kTenSecondTimeoutInMS ); } ResetMediaCharacteristics ( ); messageClients ( kIOMessageTrayStateChange, ( void * ) kMessageTrayStateChangeRequestAccepted ); if ( fLowPowerPollingEnabled == false ) { // Set the polling to determine when new media has been inserted fPollingMode = kPollingMode_NewMedia; TicklePowerManager ( ); EnablePolling ( ); } } ReleaseSCSITask ( request ); status = kIOReturnSuccess; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ FormatMedia - Unsupported. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::FormatMedia ( UInt64 byteCapacity ) { IOReturn status = kIOReturnUnsupported; return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetFormatCapacities - Unsupported. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt32 IOSCSIMultimediaCommandsDevice::GetFormatCapacities ( UInt64 * capacities, UInt32 capacitiesMaxCount ) const { return 0; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ LockUnlockMedia - Unsupported. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::LockUnlockMedia ( bool doLock ) { IOReturn status = kIOReturnSuccess; require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnNotAttached ); require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnOffline ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SynchronizeCache - Synchronizes the write cache. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SynchronizeCache ( void ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSITaskIdentifier request = NULL; IOReturn status = kIOReturnNoResources; STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( SYNCHRONIZE_CACHE ( request, 0, 0, 0, 0, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnInternalError; } ReleaseSCSITask ( request ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportBlockSize - Reports the medium block size. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportBlockSize ( UInt64 * blockSize ) { *blockSize = fMediaBlockSize; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportEjectability - Reports the medium ejectability characteristic. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportEjectability ( bool * isEjectable ) { *isEjectable = fMediaIsRemovable; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportLockability - Reports the medium lockability characteristic. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportLockability ( bool * isLockable ) { *isLockable = true; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportPollRequirements - Reports polling requirements (none). // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportPollRequirements ( bool * pollIsRequired, bool * pollIsExpensive ) { // Since we have our own polling code, we do not need to // have the Storage Family poll us for media changes. We use // asynchronous media notifications instead. We have custom // polling code because there are several manual eject devices // which fail the PREVENT_ALLOW_MEDIUM_REMOVAL command and // we must continue to poll them for unexpected media removal. *pollIsRequired = false; *pollIsExpensive = false; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportMaxReadTransfer - Reports maximum read transfer in bytes. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportMaxReadTransfer ( UInt64 blockSize, UInt64 * max ) { UInt32 maxBlockCount = kDefaultMaxBlocksPerIO; UInt64 maxByteCount = 0; bool supported = false; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReportMaxReadTransfer.\n" ) ); // See if the transport driver wants us to limit the block transfer count supported = GetProtocolDriver ( )->IsProtocolServiceSupported ( kSCSIProtocolFeature_MaximumReadBlockTransferCount, &maxBlockCount ); if ( supported == false ) maxBlockCount = kDefaultMaxBlocksPerIO; // See if the transport driver wants us to limit the transfer byte count supported = GetProtocolDriver ( )->IsProtocolServiceSupported ( kSCSIProtocolFeature_MaximumReadTransferByteCount, &maxByteCount ); if ( ( supported == true ) && ( maxByteCount > 0 ) && ( fMediaBlockSize > 0 ) ) { maxBlockCount = min ( maxBlockCount, ( maxByteCount / fMediaBlockSize ) ); } *max = maxBlockCount * blockSize; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportMaxWriteTransfer - Reports maximum write transfer in bytes. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportMaxWriteTransfer ( UInt64 blockSize, UInt64 * max ) { UInt32 maxBlockCount = kDefaultMaxBlocksPerIO; UInt64 maxByteCount = 0; bool supported = false; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReportMaxWriteTransfer.\n" ) ); // See if the transport driver wants us to limit the block transfer count supported = GetProtocolDriver ( )->IsProtocolServiceSupported ( kSCSIProtocolFeature_MaximumWriteBlockTransferCount, &maxBlockCount ); if ( supported == false ) maxBlockCount = kDefaultMaxBlocksPerIO; // See if the transport driver wants us to limit the transfer byte count supported = GetProtocolDriver ( )->IsProtocolServiceSupported ( kSCSIProtocolFeature_MaximumWriteTransferByteCount, &maxByteCount ); if ( ( supported == true ) && ( maxByteCount > 0 ) && ( fMediaBlockSize > 0 ) ) { maxBlockCount = min ( maxBlockCount, ( maxByteCount / fMediaBlockSize ) ); } *max = maxBlockCount * blockSize; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportMaxValidBlock - Reports maximum valid block on the media. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportMaxValidBlock ( UInt64 * maxBlock ) { if ( fMediaBlockCount == 0 ) { // If the capacity is zero, return that for // the max valid block. *maxBlock = 0; } else { // Since the driver stores the number of blocks, and // blocks are addressed starting at zero, subtract one // to get the maximum valid block. *maxBlock = fMediaBlockCount - 1; } STATUS_LOG ( ( "%s::%s maxBlockHi = 0x%x, maxBlockLo = 0x%x\n", getName ( ), __FUNCTION__, ( *maxBlock ), ( *maxBlock ) >> 32 ) ); return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportMediaState - Reports state of media in the device [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportMediaState ( bool * mediaPresent, bool * changed ) { *mediaPresent = fMediaPresent; *changed = fMediaChanged; if ( fMediaChanged ) { fMediaChanged = !fMediaChanged; } return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportRemovability - Reports removability characteristic of media // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportRemovability ( bool * isRemovable ) { *isRemovable = fMediaIsRemovable; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportWriteProtection - Reports write protection characteristic of media // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportWriteProtection ( bool * isWriteProtected ) { *isWriteProtected = fMediaIsWriteProtected; return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetTrayState - Reports the current tray state of the device (if possible) // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetTrayState ( UInt8 * trayState ) { IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; UInt8 statusBuffer[8] = { 0 }; IOMemoryDescriptor * buffer = NULL; buffer = IOMemoryDescriptor::withAddress ( statusBuffer, 8, kIODirectionIn ); require_nonzero ( buffer, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( GET_EVENT_STATUS_NOTIFICATION ( request, buffer, 1, 1 << 4, /* media status notification event */ 8, 0x00 ) == true ) { serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "GET_EVENT_STATUS_NOTIFICATION succeeded.\n" ) ); *trayState = statusBuffer[5] & 0x01; STATUS_LOG ( ( "trayState = %d.\n", *trayState ) ); status = kIOReturnSuccess; } else { // The device doesn't support the GET_EVENT_STATUS_NOTIFICATION. // Assume the tray is shut. ERROR_LOG ( ( "GET_EVENT_STATUS_NOTIFICATION failed.\n" ) ); *trayState = 0; STATUS_LOG ( ( "trayState = %d.\n", *trayState ) ); status = kIOReturnSuccess; } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero_quiet ( buffer, ErrorExit ); buffer->release ( ); buffer = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetTrayState - Sets the tray state of the device (if possible) // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SetTrayState ( UInt8 trayState ) { IOReturn status = kIOReturnNotPermitted; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require ( ( fMediaPresent == false ), ErrorExit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); // Set to desired tray state. if ( START_STOP_UNIT ( request, 1, 0, 1, !trayState, 0 ) == true ) { serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "START_STOP_UNIT succeeded.\n" ) ); status = kIOReturnSuccess; } ReleaseSCSITask ( request ); messageClients ( kIOMessageTrayStateChange, ( void * ) kMessageTrayStateChangeRequestAccepted ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AsyncReadCD - Issues an asynchronous READ_CD request. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AsyncReadCD ( IOMemoryDescriptor * buffer, UInt32 startBlock, UInt32 blockCount, CDSectorArea sectorArea, CDSectorType sectorType, void * clientData ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; STATUS_LOG ( ( "%s::%s Attempted\n", getName ( ), __FUNCTION__ ) ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( READ_CD ( request, buffer, sectorType, 0, startBlock, blockCount, ( ( sectorArea & kCDSectorAreaSync ) ? 0x1 : 0x0 ), ( ( sectorArea & kCDSectorAreaHeader ) ? 0x1 : 0x0 ) | ( ( sectorArea & kCDSectorAreaSubHeader ) ? 0x2 : 0x0 ), ( ( sectorArea & kCDSectorAreaUser ) ? 0x1 : 0x0 ), ( ( sectorArea & kCDSectorAreaAuxiliary ) ? 0x1 : 0x0 ), ( ( sectorArea & kCDSectorAreaErrorFlags ) ? 0x1 : 0x0 ), ( ( sectorArea & kCDSectorAreaSubChannel ) ? 0x1 : 0x0 ) | ( ( sectorArea & kCDSectorAreaSubChannelQ ) ? 0x2 : 0x0 ), 0 ) == true ) { SetApplicationLayerReference ( request, clientData ); // The command was successfully built, now send it SendCommand ( request, fReadTimeoutDuration, &IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete ); status = kIOReturnSuccess; } else { status = kIOReturnBadArgument; ReleaseSCSITask ( request ); request = NULL; } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadISRC - Reads the ISRC (International Standard Recording Code) // from the media. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadISRC ( UInt8 track, CDISRC isrc ) { IOReturn status = kIOReturnNoResources; IOMemoryDescriptor * desc = NULL; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; UInt8 isrcData[kSubChannelDataBufferSize]; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadISRC called\n" ) ); desc = IOMemoryDescriptor::withAddress ( isrcData, kSubChannelDataBufferSize, kIODirectionIn ); require_nonzero ( desc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( READ_SUB_CHANNEL ( request, desc, 1, 1, 0x03, track, kSubChannelDataBufferSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { // Check if we found good data. if ( isrcData[8] & kTrackCatalogValueFieldValidMask ) { bcopy ( &isrcData[9], isrc, kCDISRCMaxLength ); isrc[kCDISRCMaxLength] = 0; status = kIOReturnSuccess; } else { status = kIOReturnNotFound; } } else { status = kIOReturnNotFound; } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero_quiet ( desc, ErrorExit ); desc->release ( ); desc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadMCN - Reads the MCN (Media Catalogue Number) from the media. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadMCN ( CDMCN mcn ) { IOReturn status = kIOReturnNoResources; IOMemoryDescriptor * desc = NULL; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; UInt8 mcnData[kSubChannelDataBufferSize]; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadMCN called\n" ) ); desc = IOMemoryDescriptor::withAddress ( mcnData, kSubChannelDataBufferSize, kIODirectionIn ); require_nonzero ( desc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( READ_SUB_CHANNEL ( request, desc, 1, 1, 0x02, 0, kSubChannelDataBufferSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { // Check if we found good data. if ( mcnData[8] & kMediaCatalogValueFieldValidMask ) { bcopy ( &mcnData[9], mcn, kCDMCNMaxLength ); mcn[kCDMCNMaxLength] = 0; status = kIOReturnSuccess; } else { status = kIOReturnNotFound; } } else { status = kIOReturnNotFound; } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero_quiet ( desc, ErrorExit ); desc->release ( ); desc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadTOC - Reads the Table Of Contents, Format 0x02, from the media. // *OBSOLETE* Callers should use the other ReadTOC API. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadTOC ( IOMemoryDescriptor * buffer ) { return ReadTOC ( buffer, kCDTOCFormatTOC, 0x01, 0x00, NULL ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadTOC - Reads the specified format of the Table Of Contents from // the media. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadTOC ( IOMemoryDescriptor * buffer, CDTOCFormat format, UInt8 msf, UInt32 trackSessionNumber, UInt16 * actualByteCount ) { IOBufferMemoryDescriptor * doubleBuffer = NULL; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOReturn status = kIOReturnError; IOMemoryDescriptor * bufferToUse = NULL; if ( ( format == kCDTOCFormatTOC ) && ( msf == 1 ) ) { // If they ask for 4 bytes, use 0xFFFE to make sure we get the whole TOC if ( buffer->getLength ( ) == sizeof ( CDTOC ) ) { UInt8 * zeroPtr = 0; STATUS_LOG ( ( "2ble buffer using 0xFFFE as size\n" ) ); doubleBuffer = IOBufferMemoryDescriptor::withCapacity ( 0xFFFE, kIODirectionIn ); require_nonzero_action ( doubleBuffer, ErrorExit, status = kIOReturnNoResources ); bufferToUse = doubleBuffer; zeroPtr = ( UInt8 * ) doubleBuffer->getBytesNoCopy ( ); bzero ( zeroPtr, doubleBuffer->getLength ( ) ); } // If they ask for an odd number of bytes, pad it to make it even else if ( ( buffer->getLength ( ) & 1 ) == 1 ) { UInt8 * zeroPtr = 0; STATUS_LOG ( ( "2ble buffer using %ld as size\n", buffer->getLength ( ) + 1 ) ); doubleBuffer = IOBufferMemoryDescriptor::withCapacity ( buffer->getLength ( ) + 1, kIODirectionIn ); require_nonzero_action ( doubleBuffer, ErrorExit, status = kIOReturnNoResources ); bufferToUse = doubleBuffer; zeroPtr = ( UInt8 * ) doubleBuffer->getBytesNoCopy ( ); bzero ( zeroPtr, doubleBuffer->getLength ( ) ); } // Else, it's all good... else { STATUS_LOG ( ( "No 2ble buffer\n" ) ); bufferToUse = buffer; } } else { STATUS_LOG ( ( "No 2ble buffer\n" ) ); bufferToUse = buffer; } request = GetSCSITask ( ); require_nonzero_action ( request, ReleaseDescriptor, status = kIOReturnNoResources ); if ( READ_TOC_PMA_ATIP ( request, bufferToUse, msf, // MSF bit set format, 0, bufferToUse->getLength ( ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { if ( actualByteCount != NULL ) { *actualByteCount = GetRealizedDataTransferCount ( request ); } if ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) { status = kIOReturnSuccess; if ( ( format == kCDTOCFormatTOC ) && ( msf == 1 ) ) { UInt8 * ptr; UInt16 sizeOfTOC; IOByteCount bufLength; // not used ptr = ( UInt8 * ) bufferToUse->getVirtualSegment ( 0, &bufLength ); // Get the size of the TOC data returned sizeOfTOC = OSReadBigInt16 ( ptr, 0 ) + sizeof ( UInt16 ); // We have successfully gotten a TOC, check if the driver was able to // determine a block count from READ CAPACITY. If the media has a reported // block count of zero, then this is most likely one of the devices that does // not correctly update the capacity information after writing a disc. Since the // BCD conversion check cannot be successfully done without valid capacity // data, do neither the check nor the conversion. if ( fMediaBlockCount != 0 ) { UInt8 lastSessionNum; UInt32 index; UInt8 * beginPtr; bool needsBCDtoHexConv = false; UInt32 numLBAfromMSF; beginPtr = ptr; #if ( SCSI_MMC_DEVICE_DEBUGGING_LEVEL >= 2 ) if ( sizeOfTOC > GetRealizedDataTransferCount ( request ) ) ERROR_LOG ( ( "sizeOfTOC != Realized Data Transfer Count\n size = %d, xfer count = %ld\n", sizeOfTOC, ( UInt32 ) GetRealizedDataTransferCount ( request ) ) ); #endif require_action ( ( sizeOfTOC > sizeof ( CDTOC ) ), ReleaseTask, status = kIOReturnError ); // Get the number of the last session on the disc. // Since this number will be match to the appropriate // data in the returned TOC, the format of this (Hex or BCD) // has no impact on its use. lastSessionNum = ptr[3]; // Find the A2 point for the last session for ( index = 4; index < ( UInt32 ) ( sizeOfTOC - 4 ); index += 11 ) { // Check if this Track Descriptor is for the last session if ( ptr[index] != lastSessionNum ) { // Not for the last session, go on to the next descriptor. continue; } // If we got here, then this Track Descriptor is for the last session, // now check to see if it is the A2 point. if ( ptr[index + 3] != 0xA2 ) { continue; } // If we got here, then this Track Descriptor is for the last session, // and is the A2 point. Now check if the beginning of the lead out is greater // than the disc capacity (plus the 2 second leadin or 150 blocks) plus 75 // sector tolerance. // ₯₯₯ The spec says the tolerance should only be considered if the last // track is audio. Fix this when we get time numLBAfromMSF = ( ( ( ptr[index + 8] * 60 ) + ( ptr[index + 9] ) ) * 75 ) + ( ptr[index + 10] ); if ( numLBAfromMSF > ( ( fMediaBlockCount + 150 ) + 75 ) ) { needsBCDtoHexConv = true; break; } } if ( needsBCDtoHexConv == true ) { ERROR_LOG ( ( "Drive needs BCD->HEX conversion\n" ) ); // Convert First/Last session info ptr[2] = ConvertBCDToHex ( ptr[2] ); ptr[3] = ConvertBCDToHex ( ptr[3] ); ptr = &ptr[4]; // Loop over track descriptors finding the BCD values and change them to hex. for ( index = 0; index < ( UInt32 ) ( sizeOfTOC - 4 ); index += 11 ) { if ( ( ptr[index + 3] == 0xA0 ) || ( ptr[index + 3] == 0xA1 ) ) { // Fix the A0 and A1 PMIN values. ptr[index + 8] = ConvertBCDToHex ( ptr[index + 8] ); } else { // Fix the Point value field ptr[index + 3] = ConvertBCDToHex ( ptr[index + 3] ); // Fix the Minutes value field ptr[index + 8] = ConvertBCDToHex ( ptr[index + 8] ); // Fix the Seconds value field ptr[index + 9] = ConvertBCDToHex ( ptr[index + 9] ); // Fix the Frames value field ptr[index + 10] = ConvertBCDToHex ( ptr[index + 10] ); } } } if ( bufferToUse != buffer ) { STATUS_LOG ( ( "Writing Bytes\n" ) ); buffer->writeBytes ( 0, beginPtr, min ( buffer->getLength ( ), sizeOfTOC ) ); } } else { require_action ( ( sizeOfTOC > sizeof ( CDTOC ) ), ReleaseTask, status = kIOReturnError ); } } } } else { if ( actualByteCount != NULL ) { *actualByteCount = 0; } // We got an error on the READ_TOC_PMA_ATIP. We shouldn't get one unless the media // is blank, so return an error. status = kIOReturnIOError; } ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( doubleBuffer, ErrorExit ); doubleBuffer->release ( ); doubleBuffer = NULL; ErrorExit: STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadTOC status = %d\n", status ) ); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadDiscInfo - Performs a READ_DISC_INFO command to get information // about the media. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadDiscInfo ( IOMemoryDescriptor * buffer, UInt16 * actualByteCount ) { IOReturn status = kIOReturnIOError; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadDiscInfo called\n" ) ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( READ_DISC_INFORMATION ( request, buffer, buffer->getLength ( ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { if ( actualByteCount != NULL ) { *actualByteCount = GetRealizedDataTransferCount ( request ); } if ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) { status = kIOReturnSuccess; } } else if ( actualByteCount != NULL ) { *actualByteCount = 0; } ReleaseSCSITask ( request ); ErrorExit: STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadDiscInfo status = %d\n", status ) ); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadTrackInfo - Performs a READ_TRACK_INFO command to get information // about the media. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadTrackInfo ( IOMemoryDescriptor * buffer, UInt32 address, CDTrackInfoAddressType addressType, UInt16 * actualByteCount ) { IOReturn status = kIOReturnIOError; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadTrackInfo called\n" ) ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( READ_TRACK_INFORMATION ( request, buffer, addressType, address, buffer->getLength ( ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { if ( actualByteCount != NULL ) { *actualByteCount = GetRealizedDataTransferCount ( request ); } if ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) { status = kIOReturnSuccess; } } else if ( actualByteCount != NULL ) { *actualByteCount = 0; } ReleaseSCSITask ( request ); ErrorExit: STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadTrackInfo status = %d\n", status ) ); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AudioPause - Pauses analog audio playback. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AudioPause ( bool pause ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( PAUSE_RESUME ( request, !pause, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AudioPlay - Starts analog audio playback. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AudioPlay ( CDMSF timeStart, CDMSF timeStop ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSICmdField4Byte STARTING_MSF = 0; SCSICmdField4Byte ENDING_MSF = 0; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); // Do some bit shifting to be endian neutral STARTING_MSF = ( timeStart.minute << 24 ) | ( timeStart.second << 16 ) | ( timeStart.frame << 8 ); ENDING_MSF = ( timeStop.minute << 24 ) | ( timeStop.second << 16 ) | ( timeStop.frame << 8 ); // These are multi-byte fields, so use OSWriteBigInt32 to make them correct. OSWriteBigInt32 ( &STARTING_MSF, 0, STARTING_MSF ); OSWriteBigInt32 ( &ENDING_MSF, 0, ENDING_MSF ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( PLAY_AUDIO_MSF ( request, STARTING_MSF, ENDING_MSF, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AudioScan - Starts analog audio scanning. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AudioScan ( CDMSF timeStart, bool reverse ) { IOReturn status = kIOReturnUnsupported; SCSICmdField4Byte SCAN_STARTING_ADDRESS_FIELD = 0; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); // Do some bit shifting to be endian neutral SCAN_STARTING_ADDRESS_FIELD = ( timeStart.minute << 24 ) | ( timeStart.second << 16 ) | ( timeStart.frame << 8 ); // Use OSWriteBigInt32 to make sure it is written correctly. OSWriteBigInt32 ( &SCAN_STARTING_ADDRESS_FIELD, 0, SCAN_STARTING_ADDRESS_FIELD ); if ( SCAN ( request, reverse, 0, SCAN_STARTING_ADDRESS_FIELD, 0x01, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AudioStop - Stops analog audio playback. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::AudioStop ( void ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( STOP_PLAY_SCAN ( request, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetAudioStatus - Unsupported // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetAudioStatus ( CDAudioStatus * status ) { return kIOReturnUnsupported; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetAudioVolume - Gets the analog audio volume. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetAudioVolume ( UInt8 * leftVolume, UInt8 * rightVolume ) { IOReturn status = kIOReturnUnsupported; IOMemoryDescriptor * bufferDesc = NULL; bool use10Byte = true; UInt8 cdAudioModePageBuffer[kCDAudioModePageBufferSize]; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); bufferDesc = IOMemoryDescriptor::withAddress ( cdAudioModePageBuffer, kCDAudioModePageBufferSize, kIODirectionIn ); require_nonzero_action ( bufferDesc, ErrorExit, status = kIOReturnNoResources ); status = GetModeSense ( bufferDesc, kCDAudioModePageCode, kCDAudioModePageBufferSize, &use10Byte ); if ( status == kIOReturnSuccess ) { *leftVolume = cdAudioModePageBuffer[17]; *rightVolume = cdAudioModePageBuffer[19]; } bufferDesc->release ( ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetAudioVolume - Sets the analog audio volume. // *OBSOLETE* All CD playback is digital now. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SetAudioVolume ( UInt8 leftVolume, UInt8 rightVolume ) { IOReturn status = kIOReturnUnsupported; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; bool commandOK = false; UInt8 cdAudioModePageBuffer[kCDAudioModePageBufferSize] = { 0 }; bool use10Byte = true; require_nonzero ( ( fSupportedCDFeatures & kCDFeaturesAnalogAudioMask ), Exit ); bufferDesc = IOMemoryDescriptor::withAddress ( cdAudioModePageBuffer, kCDAudioModePageBufferSize, kIODirectionIn ); require_nonzero_action ( bufferDesc, ErrorExit, status = kIOReturnNoResources ); status = GetModeSense ( bufferDesc, kCDAudioModePageCode, kCDAudioModePageBufferSize, &use10Byte ); bufferDesc->release ( ); bufferDesc = NULL; require_success ( status, ErrorExit ); bufferDesc = IOMemoryDescriptor::withAddress ( cdAudioModePageBuffer, kCDAudioModePageBufferSize, kIODirectionOut ); require_nonzero_action ( bufferDesc, ErrorExit, status = kIOReturnNoResources ); request = GetSCSITask ( ); require_nonzero_action ( request, ReleaseDescriptor, status = kIOReturnNoResources ); cdAudioModePageBuffer[9] = kCDAudioModePageCode; cdAudioModePageBuffer[17] = leftVolume; cdAudioModePageBuffer[19] = rightVolume; if ( GetANSIVersion ( ) == kINQUIRY_ANSI_VERSION_NoClaimedConformance ) { commandOK = MODE_SELECT_10 ( request, bufferDesc, 0x01, 0x00, kCDAudioModePageBufferSize, 0 ); } else { commandOK = MODE_SELECT_6 ( request, bufferDesc, 0x01, 0x00, kCDAudioModePageBufferSize, 0 ); } if ( commandOK ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetMediaType - Returns the media type. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt32 IOSCSIMultimediaCommandsDevice::GetMediaType ( void ) { return fMediaType; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReportKey - Issues a REPORT_KEY to the device. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReportKey ( IOMemoryDescriptor * buffer, const DVDKeyClass keyClass, const UInt32 lba, const UInt8 agid, const DVDKeyFormat keyFormat ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require_nonzero ( ( fSupportedDVDFeatures & kDVDFeaturesCSSMask ), Exit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( REPORT_KEY ( request, buffer, ( keyFormat == 0x04 ) ? lba : 0, ( buffer != NULL ) ? buffer->getLength ( ) : 0, agid, keyFormat, 0x00 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { SCSI_Sense_Data senseDataBuffer; bool senseIsValid; senseIsValid = GetAutoSenseData ( request, &senseDataBuffer ); if ( senseIsValid ) { IOLog ( "REPORT_KEY failed : ASC = 0x%02x, ASCQ = 0x%02x\n", senseDataBuffer.ADDITIONAL_SENSE_CODE, senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ); } status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SendKey - Issues a SEND_KEY to the device. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SendKey ( IOMemoryDescriptor * buffer, const DVDKeyClass keyClass, const UInt8 agid, const DVDKeyFormat keyFormat ) { IOReturn status = kIOReturnUnsupported; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; require_nonzero ( ( fSupportedDVDFeatures & kDVDFeaturesCSSMask ), Exit ); request = GetSCSITask ( ); require_nonzero_action ( request, ErrorExit, status = kIOReturnNoResources ); if ( SEND_KEY ( request, buffer, ( buffer != NULL ) ? buffer->getLength ( ) : 0, agid, keyFormat, 0x00 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { SCSI_Sense_Data senseDataBuffer; bool senseIsValid; senseIsValid = GetAutoSenseData ( request, &senseDataBuffer ); if ( senseIsValid ) { IOLog ( "SEND_KEY failed : ASC = 0x%02x, ASCQ = 0x%02x\n", senseDataBuffer.ADDITIONAL_SENSE_CODE, senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ); } status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: Exit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReadDVDStructure - Issues a READ_DVD_STRUCTURE to the device. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ReadDVDStructure ( IOMemoryDescriptor * buffer, const UInt32 length, const UInt8 structureFormat, const UInt32 logicalBlockAddress, const UInt8 layer, const UInt8 agid ) { IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( READ_DVD_STRUCTURE ( request, buffer, logicalBlockAddress, layer, structureFormat, length, agid, 0x00 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { SCSI_Sense_Data senseDataBuffer; bool senseIsValid; senseIsValid = GetAutoSenseData ( request, &senseDataBuffer ); if ( senseIsValid ) { IOLog ( "READ_DVD_STRUCTURE failed : ASC = 0x%02x, ASCQ = 0x%02x\n", senseDataBuffer.ADDITIONAL_SENSE_CODE, senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ); } status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetMediaAccessSpeed - Obtains the media access speed. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetMediaAccessSpeed ( UInt16 * kilobytesPerSecond ) { IOReturn status = kIOReturnSuccess; UInt32 actualSize = 0; UInt32 minBufferSize = 0; UInt16 blockDescriptorLength = 0; UInt8 pageCode = 0; UInt8 * mechanicalCapabilities = NULL; UInt8 headerSize = 0; IOBufferMemoryDescriptor * bufferDesc = NULL; bool use10Byte = true; status = GetMechanicalCapabilitiesSize ( &actualSize ); require_success ( status, ErrorExit ); bufferDesc = IOBufferMemoryDescriptor::withCapacity ( actualSize, kIODirectionIn, true ); require_nonzero_action ( bufferDesc, ErrorExit, status = kIOReturnNoResources ); mechanicalCapabilities = ( UInt8 * ) bufferDesc->getBytesNoCopy ( ); bzero ( mechanicalCapabilities, actualSize ); status = GetModeSense ( bufferDesc, kMechanicalCapabilitiesModePageCode, actualSize, &use10Byte ); require_success ( status, ReleaseDescriptor ); if ( use10Byte ) { // We check to make sure there aren't any block descriptors. If there // are, we skip over them. blockDescriptorLength = OSReadBigInt16 ( mechanicalCapabilities, 6 ); headerSize = kModeSense10ParameterHeaderSize; } else { blockDescriptorLength = mechanicalCapabilities[3]; headerSize = kModeSense6ParameterHeaderSize; } // Ensure that our buffer is of the minimum correct size. // Also, check that the first byte of the page is the // correct PAGE_CODE (kMechanicalCapabilitiesModePageCode). minBufferSize = headerSize + blockDescriptorLength + kMediaAccessSpeedOffset + sizeof ( UInt16 ); require_action ( ( actualSize >= minBufferSize ), ReleaseDescriptor, status = kIOReturnInternalError ); pageCode = mechanicalCapabilities[headerSize + blockDescriptorLength] & 0x3F; require_action ( ( pageCode == kMechanicalCapabilitiesModePageCode ), ReleaseDescriptor, status = kIOReturnInternalError ); *kilobytesPerSecond = OSReadBigInt16 ( mechanicalCapabilities, headerSize + blockDescriptorLength + kMediaAccessSpeedOffset ); status = kIOReturnSuccess; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetMediaAccessSpeed - Sets the media access speed. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::SetMediaAccessSpeed ( UInt16 kilobytesPerSecond ) { IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); switch ( fMediaType ) { case kCDMediaTypeROM: case kCDMediaTypeR: case kCDMediaTypeRW: if ( SET_CD_SPEED ( request, kilobytesPerSecond, 0, 0 ) == true ) { serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } break; case kDVDMediaTypeROM: case kDVDMediaTypeRAM: case kDVDMediaTypeR: case kDVDMediaTypeRW: case kDVDMediaTypePlusRW: if ( SET_CD_SPEED ( request, kilobytesPerSecond, 0, 0x40 ) == true ) { serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } break; default: break; } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { fCurrentDiscSpeed = kilobytesPerSecond; status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: return status; } #if 0 #pragma mark - #pragma mark ₯ Protected Methods - Methods used by this class and subclasses #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ InitializeDeviceSupport - Initializes device support [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::InitializeDeviceSupport ( void ) { bool setupSuccessful = false; // Initialize the device characteristics flags fSupportedCDFeatures = 0; fSupportedDVDFeatures = 0; fDeviceSupportsLowPowerPolling = false; fMediaChanged = false; fMediaPresent = false; fMediaIsRemovable = false; fMediaType = kCDMediaTypeUnknown; fMediaIsWriteProtected = true; fCurrentDiscSpeed = 0; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::InitializeDeviceSupport called\n" ) ); fIOSCSIMultimediaCommandsDeviceReserved = IONew ( IOSCSIMultimediaCommandsDeviceExpansionData, 1 ); require_nonzero ( fIOSCSIMultimediaCommandsDeviceReserved, ErrorExit ); bzero ( fIOSCSIMultimediaCommandsDeviceReserved, sizeof ( IOSCSIMultimediaCommandsDeviceExpansionData ) ); // Make sure the drive is ready for us! require ( ClearNotReadyStatus ( ), ReleaseExpansionData ); setupSuccessful = DetermineDeviceCharacteristics ( ); if ( setupSuccessful == true ) { fPollingMode = kPollingMode_NewMedia; fPollingThread = thread_call_allocate ( ( thread_call_func_t ) IOSCSIMultimediaCommandsDevice::sPollForMedia, ( thread_call_param_t ) this ); require_nonzero_action ( fPollingThread, ReleaseExpansionData, setupSuccessful = false ); InitializePowerManagement ( GetProtocolDriver ( ) ); } STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::InitializeDeviceSupport setupSuccessful = %d\n", setupSuccessful ) ); setProperty ( kIOMaximumBlockCountReadKey, kDefaultMaxBlocksPerIO, 64 ); setProperty ( kIOMaximumBlockCountWriteKey, kDefaultMaxBlocksPerIO, 64 ); return setupSuccessful; ReleaseExpansionData: require_nonzero_quiet ( fIOSCSIMultimediaCommandsDeviceReserved, ErrorExit ); IODelete ( fIOSCSIMultimediaCommandsDeviceReserved, IOSCSIMultimediaCommandsDeviceExpansionData, 1 ); fIOSCSIMultimediaCommandsDeviceReserved = NULL; ErrorExit: return setupSuccessful; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ StartDeviceSupport - Starts device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::StartDeviceSupport ( void ) { OSBoolean * shouldNotPoll = NULL; shouldNotPoll = OSDynamicCast ( OSBoolean, getProperty ( kAppleKeySwitchProperty ) ); if ( shouldNotPoll != NULL ) { // See if we should not poll. require ( shouldNotPoll->isFalse ( ), Exit ); } EnablePolling ( ); Exit: CreateStorageServiceNub ( ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SuspendDeviceSupport - Suspends device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::SuspendDeviceSupport ( void ) { if ( fPollingMode != kPollingMode_Suspended ) { DisablePolling ( ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ResumeDeviceSupport - Resumes device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::ResumeDeviceSupport ( void ) { if ( fMediaPresent == false ) { fPollingMode = kPollingMode_NewMedia; EnablePolling ( ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ StopDeviceSupport - Stops device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::StopDeviceSupport ( void ) { DisablePolling ( ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ TerminateDeviceSupport - Terminates device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::TerminateDeviceSupport ( void ) { STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::cleanUp called.\n" ) ); if ( fPollingThread != NULL ) { thread_call_free ( fPollingThread ); fPollingThread = NULL; } // Release all memory/objects associated with the reserved fields. if ( fPowerDownNotifier != NULL ) { // remove() will also call release() on this object (IONotifier). // See IONotifier.h for more info. fPowerDownNotifier->remove ( ); fPowerDownNotifier = NULL; } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CreateCommandSetObjects - Creates command set objects [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::CreateCommandSetObjects ( void ) { bool result = false; STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); fSCSIMultimediaCommandObject = SCSIMultimediaCommands::CreateSCSIMultimediaCommandObject ( ); require_nonzero ( fSCSIMultimediaCommandObject, ErrorExit ); fSCSIBlockCommandObject = SCSIBlockCommands::CreateSCSIBlockCommandObject( ); require_nonzero_action ( fSCSIBlockCommandObject, ErrorExit, fSCSIMultimediaCommandObject->release ( ) ); // We're ready to go now. result = true; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ FreeCommandSetObjects - Releases command set objects [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::FreeCommandSetObjects ( void ) { STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); if ( fSCSIMultimediaCommandObject != NULL ) { fSCSIMultimediaCommandObject->release ( ); fSCSIMultimediaCommandObject = NULL; } if ( fSCSIBlockCommandObject != NULL ) { fSCSIBlockCommandObject->release ( ); fSCSIBlockCommandObject = NULL; } // Release the reserved structure. Since this function is called from // free(), we just get rid of the stuff here. if ( fIOSCSIMultimediaCommandsDeviceReserved != NULL ) { IODelete ( fIOSCSIMultimediaCommandsDeviceReserved, IOSCSIMultimediaCommandsDeviceExpansionData, 1 ); fIOSCSIMultimediaCommandsDeviceReserved = NULL; } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetSCSIMultimediaCommandObject - Accessor method [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIMultimediaCommands * IOSCSIMultimediaCommandsDevice::GetSCSIMultimediaCommandObject ( void ) { check ( fSCSIMultimediaCommandObject ); return fSCSIMultimediaCommandObject; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetSCSIBlockCommandObject - Accessor method [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIBlockCommands * IOSCSIMultimediaCommandsDevice::GetSCSIBlockCommandObject ( void ) { check ( fSCSIBlockCommandObject ); return fSCSIBlockCommandObject; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetSCSIPrimaryCommandObject - Accessor method [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIPrimaryCommands * IOSCSIMultimediaCommandsDevice::GetSCSIPrimaryCommandObject ( void ) { check ( fSCSIMultimediaCommandObject ); return OSDynamicCast ( SCSIPrimaryCommands, GetSCSIMultimediaCommandObject ( ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ VerifyDeviceState - Releases command set objects [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::VerifyDeviceState ( void ) { if ( fLowPowerPollingEnabled == true ) { STATUS_LOG ( ( "Low power polling turned off\n" ) ); fLowPowerPollingEnabled = false; } if ( IsPowerManagementIntialized ( ) == true ) { STATUS_LOG ( ( "TicklePowerManager\n" ) ); TicklePowerManager ( ); } return kIOReturnSuccess; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ClearNotReadyStatus - Clears any NOT_READY status on device [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::ClearNotReadyStatus ( void ) { SCSI_Sense_Data senseBuffer = { 0 }; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; bool driveReady = false; bool result = true; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer, kSenseDefaultSize, kIODirectionIn ); check ( bufferDesc ); request = GetSCSITask ( ); check ( request ); do { if ( TEST_UNIT_READY ( request, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { bool validSense = false; if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION ) { validSense = GetAutoSenseData ( request, &senseBuffer ); if ( validSense == false ) { if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { validSense = true; } } if ( validSense == true ) { if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_NOT_READY ) && ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x01 ) ) { STATUS_LOG ( ( "%s::drive not ready\n", getName ( ) ) ); driveReady = false; IOSleep ( 200 ); } else if ( ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_NOT_READY ) && ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x02 ) ) { // The drive needs to be spun up. Issue a START_STOP_UNIT to it. if ( START_STOP_UNIT ( request, 0x00, 0x00, 0x00, 0x01, 0x00 ) == true ) { serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } } else { driveReady = true; STATUS_LOG ( ( "%s::drive READY\n", getName ( ) ) ); } STATUS_LOG ( ( "sense data: %01x, %02x, %02x\n", ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ), senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); } } else { driveReady = true; } } else { // Command failed. Wait and try again. IOSleep ( 200 ); } // check isInactive in case device was hot unplugged during sleep // and we are in an infinite loop here } while ( ( driveReady == false ) && ( isInactive ( ) == false ) ); bufferDesc->release ( ); ReleaseSCSITask ( request ); result = isInactive ( ) ? false : true; return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ EnablePolling - Schedules the polling thread to run [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::EnablePolling ( void ) { AbsoluteTime time; // No reason to start a thread if we've been termintated require ( ( isInactive ( ) == false ) && fPollingThread, Exit ); require ( ( fPollingMode != kPollingMode_Suspended ), Exit ); require_nonzero ( fPollingThread, Exit ); // Retain ourselves so that this object doesn't go away // while we are polling retain ( ); clock_interval_to_deadline ( 1000, kMillisecondScale, &time ); thread_call_enter_delayed ( fPollingThread, time ); Exit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DisablePolling - Unschedules the polling thread if it hasn't run yet // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::DisablePolling ( void ) { // Change the polling mode fPollingMode = kPollingMode_Suspended; // Cancel the thread if it is scheduled to run require ( thread_call_cancel ( fPollingThread ), Exit ); // It was running, so we balance out the retain ( ) // with a release ( ) release ( ); Exit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ HandleSetUserClientExclusivityState - Overrides the default function in // order to handle non-exclusive // user client connections. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::HandleSetUserClientExclusivityState ( IOService * userClient, bool state ) { IOReturn status = kIOReturnSuccess; status = super::HandleSetUserClientExclusivityState ( userClient, state ); require_success ( status, ErrorExit ); status = kIOReturnExclusiveAccess; if ( state == false ) { status = message ( kSCSIServicesNotification_Resume, NULL, NULL ); messageClients ( kSCSIServicesNotification_ExclusivityChanged ); } else { if ( fMediaPresent ) { OSIterator * childList; IOService * childService; OSObject * childObject; IOService * parent; STATUS_LOG ( ( "Media is present\n" ) ); childList = getChildIterator ( gIOServicePlane ); if ( childList != NULL ) { STATUS_LOG ( ( "childList != NULL\n" ) ); while ( ( childObject = childList->getNextObject ( ) ) != NULL ) { childService = OSDynamicCast ( IOService, childObject ); if ( childService == NULL ) continue; STATUS_LOG ( ( "childService = %s\n", childService->getName ( ) ) ); childService = OSDynamicCast ( IOBlockStorageDevice, childService ); if ( childService != NULL ) { // Keep a pointer to the parent of the block storage driver for // the call to messageClient(). parent = childService; parent->retain ( ); childList->release ( ); childList = childService->getChildIterator ( gIOServicePlane ); while ( ( childObject = childList->getNextObject ( ) ) != NULL ) { childService = OSDynamicCast ( IOService, childObject ); if ( childService == NULL ) continue; STATUS_LOG ( ( "childService = %s\n", childService->getName ( ) ) ); childService = OSDynamicCast ( IOBlockStorageDriver, childService ); if ( childService == NULL ) continue; // Ask the child nicely if it can close. This allows it to say no // (if it's busy, has media mounted, etc.) without being destructive // to the state of the device. status = parent->messageClient ( kIOMessageServiceIsRequestingClose, ( IOBlockStorageDriver * ) childService ); if ( status == kIOReturnSuccess ) { message ( kSCSIServicesNotification_Suspend, NULL, NULL ); ResetMediaCharacteristics ( ); messageClients ( kSCSIServicesNotification_ExclusivityChanged ); } else { ERROR_LOG ( ( "BlockStorageDriver wouldn't close, status = %d\n", status ) ); super::HandleSetUserClientExclusivityState ( userClient, !state ); } break; } // Make sure to drop the retain() from above parent->release ( ); } } if ( childList != NULL ) childList->release ( ); } } else { // No media is present, so clear the status message ( kSCSIServicesNotification_Suspend, NULL, NULL ); status = kIOReturnSuccess; } } ErrorExit: ERROR_LOG ( ( "IOSCSIMultimediaCommandsDevice::HandleSetUserClientExclusivityState status = %d\n", status ) ); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CreateStorageServiceNub - Creates the linkage object for IOStorageFamily // to use. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::CreateStorageServiceNub ( void ) { IOService * nub = NULL; if ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask ) { // We support DVD structure reads, so create the DVD nub nub = OSTypeAlloc ( IODVDServices ); } else { // Create a CD nub instead nub = OSTypeAlloc ( IOCompactDiscServices ); } require_nonzero ( nub, ErrorExit ); nub->init ( ); require ( nub->attach ( this ), ErrorExit ); nub->start ( this ); nub->release ( ); return; ErrorExit: PANIC_NOW ( ( "IOSCSIReducedBlockCommandsDevice::CreateStorageServiceNub failed" ) ); return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineDeviceCharacteristics - Determines device characteristics // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::DetermineDeviceCharacteristics ( void ) { IOReturn status = kIOReturnSuccess; bool result = false; STATUS_LOG ( ( "%s::%s called.\n", getName ( ), __FUNCTION__ ) ); status = DetermineIfMediaIsRemovable ( ); require_success ( status, ErrorExit ); status = DetermineDeviceFeatures ( ); require_success ( status, ErrorExit ); result = true; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineIfMediaIsRemovable - Determines if media is removable // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::DetermineIfMediaIsRemovable ( void ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; UInt8 loop = 0; UInt8 inquiryBufferCount = sizeof ( SCSICmd_INQUIRY_StandardData ); SCSICmd_INQUIRY_StandardData * inquiryBuffer = NULL; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; IOReturn status = kIOReturnNoResources; inquiryBuffer = ( SCSICmd_INQUIRY_StandardData * ) IOMalloc ( inquiryBufferCount ); require_nonzero ( inquiryBuffer, ErrorExit ); bufferDesc = IOMemoryDescriptor::withAddress ( inquiryBuffer, inquiryBufferCount, kIODirectionIn ); require_nonzero ( bufferDesc, ReleaseBuffer ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); for ( loop = 0; ( loop < kMaxRetryCount ) && ( isInactive ( ) == false ); loop++ ) { if ( INQUIRY ( request, bufferDesc, 0, 0, 0x00, inquiryBufferCount, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; break; } } require_success ( status, ReleaseTask ); if ( ( inquiryBuffer->RMB & kINQUIRY_PERIPHERAL_RMB_BitMask ) == kINQUIRY_PERIPHERAL_RMB_MediumRemovable ) { STATUS_LOG ( ( "Media is removable\n" ) ); fMediaIsRemovable = true; } else { STATUS_LOG ( ( "Media is NOT removable\n" ) ); fMediaIsRemovable = false; } // Save ANSI version of the device SetANSIVersion ( inquiryBuffer->VERSION & kINQUIRY_ANSI_VERSION_Mask ); ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ReleaseBuffer ); bufferDesc->release ( ); bufferDesc = NULL; ReleaseBuffer: require_nonzero_quiet ( inquiryBuffer, ErrorExit ); require_nonzero_quiet ( inquiryBufferCount, ErrorExit ); IOFree ( ( void * ) inquiryBuffer, inquiryBufferCount ); inquiryBuffer = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineDeviceFeatures - Determines device features [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::DetermineDeviceFeatures ( void ) { IOReturn status = kIOReturnSuccess; status = GetDeviceConfiguration ( ); check ( status == kIOReturnSuccess ); status = GetMechanicalCapabilities ( ); check ( status == kIOReturnSuccess ); if ( status != kIOReturnSuccess ) { // Since it responded as a SCSI Peripheral Device Type 05 // it must at least be a CD-ROM... STATUS_LOG ( ( "Device supports CD-ROM\n" ) ); status = kIOReturnSuccess; fSupportedCDFeatures |= kCDFeaturesReadStructuresMask; } // Check to see if the device supports power conditions. CheckPowerConditionsModePage ( ); ( void ) CheckForLowPowerPollingSupport ( ); // Set Supported CD & DVD features flags setProperty ( kIOPropertySupportedCDFeatures, fSupportedCDFeatures, 32 ); setProperty ( kIOPropertySupportedDVDFeatures, fSupportedDVDFeatures, 32 ); fDeviceCharacteristicsDictionary->setObject ( kIOPropertySupportedCDFeatures, getProperty ( kIOPropertySupportedCDFeatures ) ); fDeviceCharacteristicsDictionary->setObject ( kIOPropertySupportedDVDFeatures, getProperty ( kIOPropertySupportedDVDFeatures ) ); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetDeviceConfigurationSize - Gets the size of the profile list // returned by GET_CONFIGURATION command // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetDeviceConfigurationSize ( UInt32 * size ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSITaskIdentifier request = NULL; IOMemoryDescriptor * bufferDesc = NULL; IOReturn status = kIOReturnNoResources; UInt8 featureHeader[kProfileDataLengthFieldSize] = { 0 }; STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) ); bufferDesc = IOMemoryDescriptor::withAddress ( featureHeader, kProfileDataLengthFieldSize, kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileList, kProfileDataLengthFieldSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { // Swap to proper endian-ness since we are reading a multiple-byte field *size = OSReadBigInt32 ( &featureHeader[0], 0 ) + kProfileDataLengthFieldSize; status = kIOReturnSuccess; STATUS_LOG ( ( "size = %ld\n", *size ) ); require_action ( ( *size > kProfileDataLengthFieldSize ), ReleaseTask, *size = 0; status = kIOReturnIOError ); } else { *size = 0; status = kIOReturnIOError; } ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetDeviceConfiguration - Gets the profile list returned by // GET_CONFIGURATION command and parses it. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetDeviceConfiguration ( void ) { IOMemoryDescriptor * bufferDesc = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; UInt8 numProfiles = 0; UInt8 * profilePtr = NULL; SCSITaskIdentifier request = NULL; IOReturn status = kIOReturnSuccess; UInt32 actualProfileSize = 0; status = GetDeviceConfigurationSize ( &actualProfileSize ); require_success ( status, ErrorExit ); // The number of profiles is the actual size minus the feature header // size minus the current profile size divided by the size of a profile // descriptor numProfiles = ( actualProfileSize - kProfileFeatureHeaderSize - kProfileDescriptorSize ) / kProfileDescriptorSize; require_nonzero_action ( numProfiles, ErrorExit, status = kIOReturnIOError ); STATUS_LOG ( ( "numProfiles = %d\n", numProfiles ) ); // If we have at least one profile, then we have at least a 16 byte // buffer. As an optimization, since the other GET_CONFIGURATION commands // after the one to get the profile list only need a max of 8 bytes // we use the same buffer and memory descriptor. profilePtr = ( UInt8 * ) IOMalloc ( actualProfileSize ); require_nonzero_action ( profilePtr, ErrorExit, status = kIOReturnNoResources ); bzero ( profilePtr, actualProfileSize ); bufferDesc = IOMemoryDescriptor::withAddress ( profilePtr, actualProfileSize, kIODirectionIn ); require_nonzero_action ( bufferDesc, ReleaseBuffer, status = kIOReturnNoResources ); request = GetSCSITask ( ); require_nonzero_action ( request, ReleaseDescriptor, status = kIOReturnNoResources ); if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileList, actualProfileSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { // Adjust the pointer to be beyond the header and the current profile // to avoid duplicates status = ParseFeatureList ( numProfiles, profilePtr + kProfileFeatureHeaderSize + kProfileDescriptorSize ); require_success ( status, ReleaseTask ); } // Check for Analog Audio Play Support if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileAnalogAudio, 4, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "Device supports Analog Audio \n" ) ); fSupportedCDFeatures |= kCDFeaturesAnalogAudioMask; } if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileIncrementalStreamedWrite, 4, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "Device supports Packet Writing \n" ) ); fSupportedCDFeatures |= kCDFeaturesPacketWriteMask; } // Check for CD TAO support if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileCDTAO, 8, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "Device supports TAO Write \n" ) ); fSupportedCDFeatures |= kCDFeaturesTAOWriteMask; if ( profilePtr[4] & 0x04 ) { STATUS_LOG ( ( "Device supports TAO Test Write \n" ) ); fSupportedCDFeatures |= kCDFeaturesTestWriteMask; } if ( profilePtr[4] & 0x40 ) { STATUS_LOG ( ( "Device supports TAO BUF \n" ) ); fSupportedCDFeatures |= kCDFeaturesBUFWriteMask; } } // Check for CD Mastering support if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileCDMastering, 8, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { if ( profilePtr[4] & 0x04 ) { STATUS_LOG ( ( "Device supports CD-Mastering Test Write \n" ) ); fSupportedCDFeatures |= kCDFeaturesTestWriteMask; } if ( profilePtr[4] & 0x08 ) { STATUS_LOG ( ( "Device supports CD-Mastering Raw Write \n" ) ); fSupportedCDFeatures |= kCDFeaturesRawWriteMask; } if ( profilePtr[4] & 0x20 ) { STATUS_LOG ( ( "Device supports CD-Mastering SAO Write \n" ) ); fSupportedCDFeatures |= kCDFeaturesSAOWriteMask; } if ( profilePtr[4] & 0x40 ) { STATUS_LOG ( ( "Device supports CD-Mastering BUF \n" ) ); fSupportedCDFeatures |= kCDFeaturesBUFWriteMask; } } // Check for DVD-R Write support (on DVD-R, DVD-RW, or DVD-RAM drives only) if ( ( fSupportedDVDFeatures & ( kDVDFeaturesWriteOnceMask | kDVDFeaturesRandomWriteableMask | kDVDFeaturesReWriteableMask ) ) ) { if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileDVDWrite, 8, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { if ( profilePtr[4] & 0x04 ) { STATUS_LOG ( ( "Device supports DVD-R Write Test Write \n" ) ); fSupportedDVDFeatures |= kDVDFeaturesTestWriteMask; } if ( profilePtr[4] & 0x40 ) { STATUS_LOG ( ( "Device supports DVD-R Write BUF \n" ) ); fSupportedDVDFeatures |= kDVDFeaturesBUFWriteMask; } } } // Check for DVD-CSS Support (on DVD-ROM drives only) if ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask ) { if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileDVDCSS, 4, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { STATUS_LOG ( ( "Device supports DVD-CSS \n" ) ); fSupportedDVDFeatures |= kDVDFeaturesCSSMask; } } ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ReleaseBuffer ); bufferDesc->release ( ); bufferDesc = NULL; ReleaseBuffer: require_nonzero_quiet ( profilePtr, ErrorExit ); require_nonzero_quiet ( actualProfileSize, ErrorExit ); IOFree ( profilePtr, actualProfileSize ); profilePtr = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ParseFeatureList - Parses the profile list from the GET_CONFIGURATION // command. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ParseFeatureList ( UInt32 numProfiles, UInt8 * buffer ) { UInt16 profileNumber = 0; UInt8 * profilePtr = NULL; IOReturn status = kIOReturnBadArgument; require_nonzero ( numProfiles, ErrorExit ); require_nonzero ( buffer, ErrorExit ); profilePtr = buffer; while ( numProfiles-- ) { profileNumber = OSReadBigInt16 ( profilePtr, 0 ); switch ( profileNumber ) { case kGetConfigurationCDROM_Feature: STATUS_LOG ( ( "device supports CD-ROM\n" ) ); fSupportedCDFeatures |= kCDFeaturesReadStructuresMask; break; case kGetConfigurationCDR_Feature: STATUS_LOG ( ( "device supports CD-R\n" ) ); fSupportedCDFeatures |= kCDFeaturesWriteOnceMask; break; case kGetConfigurationCDRW_Feature: STATUS_LOG ( ( "device supports CD-RW\n" ) ); fSupportedCDFeatures |= kCDFeaturesReWriteableMask; break; case kGetConfigurationDVDROM_Feature: STATUS_LOG ( ( "device supports DVD-ROM\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesReadStructuresMask; break; case kGetConfigurationDVDR_Feature: STATUS_LOG ( ( "device supports DVD-R\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesWriteOnceMask; break; case kGetConfigurationDVDRAM_Feature: STATUS_LOG ( ( "device supports DVD-RAM/DVD+RW\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesRandomWriteableMask; break; case kGetConfigurationDVDRW_Feature: STATUS_LOG ( ( "device supports DVD-RW\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesReWriteableMask; break; case kGetConfigurationDVDPlusRW_Feature: STATUS_LOG ( ( "device supports DVD+RW\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesPlusRWMask; break; case kGetConfigurationDVDPlusR_Feature: STATUS_LOG ( ( "device supports DVD+R\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesPlusRMask; break; default: STATUS_LOG ( ( "unknown drive type\n" ) ); break; } profilePtr += kProfileDescriptorSize; } status = kIOReturnSuccess; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetMechanicalCapabilitiesSize - Obtains the size of the mechanical // capabilities buffer. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetMechanicalCapabilitiesSize ( UInt32 * size ) { UInt8 header[kModeSense10ParameterHeaderSize] = { 0 }; IOMemoryDescriptor * bufferDesc = NULL; IOReturn status = kIOReturnNoResources; bool use10Byte = true; UInt8 bufferSize = 0; bufferDesc = IOMemoryDescriptor::withAddress ( header, kModeSense10ParameterHeaderSize, kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); *size = 0; // Issue the mode sense commmand for the mechanical capabilities mode page. status = GetModeSense ( bufferDesc, kMechanicalCapabilitiesModePageCode, use10Byte ? kModeSense10ParameterHeaderSize : kModeSense6ParameterHeaderSize, &use10Byte ); require_success ( status, ReleaseDescriptor ); if ( use10Byte ) { *size = OSReadBigInt16 ( header, 0 ) + sizeof ( UInt16 ); bufferSize = kModeSense10ParameterHeaderSize; } else { *size = header[0] + sizeof ( UInt8 ); bufferSize = kModeSense6ParameterHeaderSize; } require_action ( ( *size > bufferSize ), ReleaseDescriptor, status = kIOReturnInternalError ); ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetMechanicalCapabilities - Obtains the mechanical capabilities // of the device. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::GetMechanicalCapabilities ( void ) { UInt8 * mechanicalCapabilities = NULL; IOBufferMemoryDescriptor * bufferDesc = NULL; UInt32 actualSize = 0; IOReturn status = kIOReturnError; UInt16 blockDescriptorLength = 0; UInt8 headerSize = 0; UInt32 minBufferSize = 0; UInt8 pageCode = 0; bool use10Byte = true; status = GetMechanicalCapabilitiesSize ( &actualSize ); require_success ( status, ErrorExit ); bufferDesc = IOBufferMemoryDescriptor::withCapacity ( actualSize, kIODirectionIn, true ); require_nonzero ( bufferDesc, ErrorExit ); mechanicalCapabilities = ( UInt8 * ) bufferDesc->getBytesNoCopy ( ); bzero ( mechanicalCapabilities, actualSize ); status = GetModeSense ( bufferDesc, kMechanicalCapabilitiesModePageCode, actualSize, &use10Byte ); require_nonzero ( bufferDesc, ReleaseDescriptor ); if ( use10Byte ) { // We check to make sure there aren't any block descriptors. If there // are, we skip over them. blockDescriptorLength = OSReadBigInt16 ( mechanicalCapabilities, 6 ); headerSize = kModeSense10ParameterHeaderSize; } else { blockDescriptorLength = mechanicalCapabilities[3]; headerSize = kModeSense6ParameterHeaderSize; } // Ensure that our buffer is of the minimum correct size. We // know we will inspect at least kMechanicalCapabilitiesMinBufferSize // bytes of data in the page. We add this to the mode sense header, // any block descriptors, and the size of the first two bytes // (which include the PAGE CODE and PAGE LENGTH). minBufferSize = headerSize + blockDescriptorLength + 2 + kMechanicalCapabilitiesMinBufferSize; require_action ( ( actualSize >= minBufferSize ), ReleaseDescriptor, status = kIOReturnInternalError ); // Also, check that the first byte of the page is the // correct PAGE_CODE (kMechanicalCapabilitiesModePageCode). pageCode = mechanicalCapabilities[headerSize + blockDescriptorLength] & 0x3F; require_action ( ( pageCode == kMechanicalCapabilitiesModePageCode ), ReleaseDescriptor, status = kIOReturnInternalError ); // We're ok. Parse the capabilities now. status = ParseMechanicalCapabilities ( mechanicalCapabilities + headerSize + blockDescriptorLength + 2 ); ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ParseMechanicalCapabilities - Parses the mechanical capabilities // of the device. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::ParseMechanicalCapabilities ( UInt8 * mechanicalCapabilities ) { IOReturn status = kIOReturnSuccess; require_nonzero_action ( mechanicalCapabilities, ErrorExit, status = kIOReturnBadArgument ); if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesDVDROMMask ) != 0 ) { STATUS_LOG ( ( "Device supports DVD-ROM\n" ) ); fSupportedDVDFeatures |= ( kDVDFeaturesReadStructuresMask | kDVDFeaturesCSSMask ); } // Hop to the next byte so we can check more capabilities mechanicalCapabilities++; if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesDVDRAMMask ) != 0 ) { STATUS_LOG ( ( "Device supports DVD-RAM\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesRandomWriteableMask; } if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesDVDRMask ) != 0 ) { STATUS_LOG ( ( "Device supports DVD-R\n" ) ); fSupportedDVDFeatures |= kDVDFeaturesWriteOnceMask; } if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesTestWriteMask ) != 0 ) { STATUS_LOG ( ( "Device supports Test Write\n" ) ); fSupportedCDFeatures |= kCDFeaturesTestWriteMask; // Check to see if the drive supports DVD-R writing or DVD-RW writing. // If so, we should claim it supports TestWrite for DVD too since we // can't tell for sure... if ( ( fSupportedDVDFeatures & kDVDFeaturesWriteOnceMask ) || ( fSupportedDVDFeatures & kDVDFeaturesReWriteableMask ) ) { fSupportedDVDFeatures |= kDVDFeaturesTestWriteMask; } } if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesCDRWMask ) != 0 ) { STATUS_LOG ( ( "Device supports CD-RW\n" ) ); fSupportedCDFeatures |= kCDFeaturesReWriteableMask; } if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesCDRMask ) != 0 ) { STATUS_LOG ( ( "Device supports CD-R\n" ) ); fSupportedCDFeatures |= kCDFeaturesWriteOnceMask; } // Hop to the next byte so we can check more capabilities mechanicalCapabilities++; if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesBUFMask ) != 0 ) { STATUS_LOG ( ( "Device supports BUF \n" ) ); fSupportedCDFeatures |= kCDFeaturesBUFWriteMask; if ( fSupportedDVDFeatures != 0 ) { fSupportedDVDFeatures |= kDVDFeaturesBUFWriteMask; } } if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesAnalogAudioMask ) != 0 ) { STATUS_LOG ( ( "Device supports Analog Audio \n" ) ); fSupportedCDFeatures |= kCDFeaturesAnalogAudioMask; } // Hop to the next byte so we can check more capabilities mechanicalCapabilities++; if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesCDDAStreamAccurateMask ) != 0 ) { STATUS_LOG ( ( "Device supports CD-DA stream accurate reads\n" ) ); fSupportedCDFeatures |= kCDFeaturesCDDAStreamAccurateMask; } // Since it responded to the CD Mechanical Capabilities Mode Page, it must at // least be a CD-ROM... STATUS_LOG ( ( "Device supports CD-ROM\n" ) ); fSupportedCDFeatures |= kCDFeaturesReadStructuresMask; ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetMediaCharacteristics - Sets the media characteristics. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::SetMediaCharacteristics ( UInt32 blockSize, UInt32 blockCount ) { UInt64 maxBytesRead = 0; UInt64 maxBytesWrite = 0; UInt64 maxBlocksRead = 0; UInt64 maxBlocksWrite = 0; STATUS_LOG ( ( "mediaBlockSize = %ld, blockCount = %ld\n", blockSize, blockCount ) ); fMediaBlockSize = blockSize; fMediaBlockCount = blockCount; ReportMaxReadTransfer ( fMediaBlockSize, &maxBytesRead ); ReportMaxWriteTransfer ( fMediaBlockSize, &maxBytesWrite ); if ( fMediaBlockSize > 0 ) { maxBlocksRead = maxBytesRead / fMediaBlockSize; maxBlocksWrite = maxBytesWrite / fMediaBlockSize; setProperty ( kIOMaximumBlockCountReadKey, maxBlocksRead, 64 ); setProperty ( kIOMaximumBlockCountWriteKey, maxBlocksWrite, 64 ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ResetMediaCharacteristics - Resets the media characteristics. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::ResetMediaCharacteristics ( void ) { fMediaBlockSize = 0; fMediaBlockCount = 0; fCurrentDiscSpeed = 0; fMediaPresent = false; fMediaType = kCDMediaTypeUnknown; fMediaIsWriteProtected = true; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PollForMedia - Tests for media presence. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::PollForMedia ( void ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSI_Sense_Data senseBuffer = { 0 }; UInt32 capacityData[2] = { 0 }; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; bool mediaFound = false; bool validSense = false; bool shouldEjectMedia = false; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); // Do a TEST_UNIT_READY to generate sense data if ( TEST_UNIT_READY ( request, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION ) { validSense = GetAutoSenseData ( request, &senseBuffer ); if ( validSense == false ) { bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer, kSenseDefaultSize, kIODirectionIn ); require_nonzero ( bufferDesc, ReleaseTask ); // Get the sense data to determine if media is present. // This will eventually use the autosense data if the // Transport Protocol supports it else issue the REQUEST_SENSE. if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } bufferDesc->release ( ); bufferDesc = NULL; } if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x00 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) { mediaFound = true; } switch ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) ) { case kSENSE_KEY_NOT_READY: // Check to make sure we shouldn't eject the disc. // Check for "Not Ready. Logical unit not ready, cause not reportable." and // "Not Ready. Medium not present." if ( ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) || ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3A ) ) { // Don't eject the disc break; } // Check for "Not Ready. Logical unit is in process of becoming ready." if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x01 ) ) { // Message clients that the unit is attempting find out if media is really there. messageClients ( kIOMessageMediaAccessChange, ( void * ) kMessageDeterminingMediaPresence ); // Don't eject the disc break; } // Check for "Not Ready. Unable to recover table-of-contents." if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x57 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) { // Don't eject the disc break; } // Check for "Not Ready. Logical unit not ready, format in progress." if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x04 ) ) { // Don't eject the disc break; } // Everything else we should eject the disc shouldEjectMedia = true; break; case kSENSE_KEY_MEDIUM_ERROR: case kSENSE_KEY_HARDWARE_ERROR: // Should eject the disc shouldEjectMedia = true; break; case kSENSE_KEY_BLANK_CHECK: if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x64 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) { // Don't eject the disc break; } // Should eject the disc shouldEjectMedia = true; break; default: break; } } else { mediaFound = true; } } if ( shouldEjectMedia == true ) { ERROR_LOG ( ( "IOSCSIMultimediaCommandsDevice::PollForMedia error occurred, ASC = 0x%02x, ASCQ = 0x%02x\n", senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); if ( START_STOP_UNIT ( request, 0, 0, 1, 0, 0 ) == true ) { // The command was successfully built, now send it ( void ) SendCommand ( request, kTenSecondTimeoutInMS ); } // Bail out after ejecting the disc and notifying the user and anyone listening // for tray state change message. Also, make sure that mediaFound is set to // false so that we clean up properly. mediaFound = false; messageClients ( kIOMessageTrayStateChange, ( void * ) kMessageTrayStateChangeRequestAccepted ); #if NOT_READY_FOR_PRIMETIME kern_return_t result = KERN_SUCCESS; // NOTE! This call below depends on the hard coded path of this KEXT. Make sure // that if the KEXT moves, this path is changed! result = KUNCUserNotificationDisplayNotice ( 0, // Timeout in seconds 0, // Flags (for later usage) kEmptyString, // IconPath (not supported yet) kEmptyString, // SoundPath (not supported yet) kLocalizationPath, // LocalizationPath kMediaEjectHeaderString, // The header kMediaEjectNoticeString, // The notice - look in Localizable.strings kOKButtonString ); #endif } require_quiet ( mediaFound, ReleaseTask ); messageClients ( kIOMessageMediaAccessChange, ( void * ) kMessageFoundMedia ); // If we got here, then we have found media if ( fMediaIsRemovable == true ) { // Lock removable media if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, 1, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } } bufferDesc = IOMemoryDescriptor::withAddress ( capacityData, 8, kIODirectionIn ); require_nonzero ( bufferDesc, ReleaseTask ); // We found media, get its capacity if ( READ_CAPACITY ( request, bufferDesc, 0, 0x00, 0, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { if ( capacityData[0] == 0 ) { // If the last block address is zero, set the characteristics with // the returned data. SetMediaCharacteristics ( capacityData[1], capacityData[0] ); } else { // If the last block address is not zero, increment it by one to // get the total number of blocks on the media. SetMediaCharacteristics ( OSSwapBigToHostInt32 ( capacityData[1] ), OSSwapBigToHostInt32 ( capacityData[0] ) + 1 ); } STATUS_LOG ( ( "%s: Media blocks: 0x%x and block size: 0x%x\n", getName ( ), fMediaBlockCount, fMediaBlockSize ) ); } else { ERROR_LOG ( ( "%s: Read Capacity failed\n", getName ( ) ) ); if ( fMediaIsRemovable == true ) { // Unlock removable media if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, 0, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); } } goto ReleaseTask; } DetermineMediaType ( ); CheckWriteProtection ( ); fMediaPresent = true; fMediaChanged = true; fPollingMode = kPollingMode_Suspended; // Message up the chain that we have media messageClients ( kIOMessageMediaStateHasChanged, ( void * ) kIOMediaStateOnline ); ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, Exit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: Exit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CheckForLowPowerPollingSupport - Checks for low power polling support // available on some ATAPI drives. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::CheckForLowPowerPollingSupport ( void ) { IOReturn status = kIOReturnNoResources; OSBoolean * boolValue = NULL; const OSSymbol * key = NULL; OSDictionary * dict = NULL; OSString * internalString = NULL; AppleFeatures appleFeaturesBuffer = { 0 }; IOMemoryDescriptor * bufferDesc = NULL; bool use10Byte = true; bufferDesc = IOMemoryDescriptor::withAddress ( &appleFeaturesBuffer, sizeof ( appleFeaturesBuffer ), kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); status = GetModeSense ( bufferDesc, kAppleFeaturesModePageCode, sizeof ( appleFeaturesBuffer ), &use10Byte ); require_success ( status, ReleaseDescriptor ); // Swap to proper endian-ness since we are reading a multiple-byte field if ( ( OSReadBigInt32 ( &appleFeaturesBuffer.signature, 0 ) == '.App' ) && ( appleFeaturesBuffer.supportsLowPowerPoll ) ) { // Device supports the Apple specific low-power polling. Make sure // to flag that attribute. STATUS_LOG ( ( "Device Supports Low Power Polling\n" ) ); fDeviceSupportsLowPowerPolling = true; } ReleaseDescriptor: require_nonzero ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; if ( fDeviceSupportsLowPowerPolling ) { fDeviceSupportsLowPowerPolling = IsProtocolServiceSupported ( kSCSIProtocolFeature_ProtocolSpecificPolling, NULL ); } dict = GetProtocolCharacteristicsDictionary ( ); if ( dict != NULL ) { key = OSSymbol::withCString ( kIOPropertyPhysicalInterconnectLocationKey ); if ( key != NULL ) { internalString = OSDynamicCast ( OSString, dict->getObject ( key ) ); key->release ( ); key = NULL; } } if ( ( internalString == NULL ) || ( !internalString->isEqualTo ( kIOPropertyInternalKey ) ) ) { // Not an internal drive, let's not use the power conditions mode page // info or low power polling fDeviceSupportsPowerConditions = false; fDeviceSupportsLowPowerPolling = false; } // If the drive is not a DVD drive, we won't use power conditions, // we'll either use ATA style sleep commands for ATAPI drives or just // spin down the drives if they are external. if ( fSupportedDVDFeatures == 0 ) { fDeviceSupportsPowerConditions = false; } boolValue = OSBoolean::withBoolean ( fDeviceSupportsLowPowerPolling ); if ( boolValue != NULL ) { fDeviceCharacteristicsDictionary->setObject ( kIOPropertyLowPowerPolling, boolValue ); boolValue->release ( ); boolValue = NULL; } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineMediaType - Determines the type of media in the device. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::DetermineMediaType ( void ) { bool mediaTypeFound = false; mediaTypeFound = CheckForDVDMediaType ( ); if ( mediaTypeFound == false ) { mediaTypeFound = CheckForCDMediaType ( ); } messageClients ( kIOMessageMediaAccessChange, ( void * ) kMessageMediaTypeDetermined ); // Set to maximum speed SetMediaAccessSpeed ( 0xFFFF ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CheckForDVDMediaType - Determines if the type of media is a DVD media // type. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::CheckForDVDMediaType ( void ) { SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOBufferMemoryDescriptor * bufferDesc = NULL; bool mediaTypeFound = false; UInt8 * buffer = NULL; UInt32 bufferSize = 0; STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::CheckForDVDMediaType ENTER\n" ) ); // If device supports READ_DVD_STRUCTURE, issue one to find // out if the media is a DVD media type require_quiet ( ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask ), Exit ); bufferSize = max ( kDVDPhysicalFormatInfoBufferSize, kProfileFeatureHeaderSize + kProfileDescriptorSize ); bufferDesc = IOBufferMemoryDescriptor::withCapacity ( bufferSize, kIODirectionIn ); require_nonzero ( bufferDesc, Exit ); buffer = ( UInt8 * ) bufferDesc->getBytesNoCopy ( ); bzero ( buffer, bufferSize ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( READ_DVD_STRUCTURE ( request, bufferDesc, 0x00, 0x00, kDVDStructureFormatPhysicalFormatInfo, kDVDPhysicalFormatInfoBufferSize, 0x00, 0x00 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { DVDPhysicalFormatInfo * physicalFormatInfo = ( DVDPhysicalFormatInfo * ) buffer; switch ( physicalFormatInfo->bookType ) { case 0: STATUS_LOG ( ( "fMediaType = DVD-ROM\n" ) ); fMediaType = kDVDMediaTypeROM; break; case 1: STATUS_LOG ( ( "fMediaType = DVD-RAM\n" ) ); fMediaType = kDVDMediaTypeRAM; break; case 2: STATUS_LOG ( ( "fMediaType = DVD-R\n" ) ); fMediaType = kDVDMediaTypeR; break; case 3: STATUS_LOG ( ( "fMediaType = DVD-RW\n" ) ); fMediaType = kDVDMediaTypeRW; break; case 8: STATUS_LOG ( ( "fMediaType = DVD+R\n" ) ); fMediaType = kDVDMediaTypePlusR; break; case 9: STATUS_LOG ( ( "fMediaType = DVD+RW\n" ) ); fMediaType = kDVDMediaTypePlusRW; break; default: STATUS_LOG ( ( "physicalFormatInfo.bookType = %d\n", physicalFormatInfo.bookType ) ); STATUS_LOG ( ( "fMediaType = kCDMediaTypeUnknown\n" ) ); break; } } if ( ( fMediaType == kCDMediaTypeUnknown ) && ( fSupportedDVDFeatures & kDVDFeaturesPlusRWMask ) ) { STATUS_LOG ( ( "Check for DVD+RW media\n" ) ); bzero ( buffer, bufferSize ); if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, // Only one feature descriptor kGetConfigurationProfileDVDPlusRW, kProfileFeatureHeaderSize + kProfileDescriptorSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { UInt8 * profilePtr; profilePtr = buffer + kProfileFeatureHeaderSize; if ( profilePtr[2] & 0x01 ) { STATUS_LOG ( ( "fMediaType = DVD+RW\n" ) ); fMediaType = kDVDMediaTypePlusRW; } } } if ( ( fMediaType == kCDMediaTypeUnknown ) && ( fSupportedDVDFeatures & kDVDFeaturesPlusRMask ) ) { STATUS_LOG ( ( "Check for DVD+R media\n" ) ); bzero ( buffer, bufferSize ); if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, // Only one feature descriptor kGetConfigurationProfileDVDPlusR, kProfileFeatureHeaderSize + kProfileDescriptorSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { UInt8 * profilePtr; profilePtr = buffer + kProfileFeatureHeaderSize; if ( profilePtr[2] & 0x01 ) { STATUS_LOG ( ( "fMediaType = DVD+R\n" ) ); fMediaType = kDVDMediaTypePlusR; } } } if ( fMediaType != kCDMediaTypeUnknown ) { mediaTypeFound = true; } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero ( bufferDesc, Exit ); bufferDesc->release ( ); bufferDesc = NULL; Exit: STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::CheckForDVDMediaType EXIT\n" ) ); return mediaTypeFound; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CheckForCDMediaType - Determines if the type of media is a CD media // type. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSIMultimediaCommandsDevice::CheckForCDMediaType ( void ) { UInt8 tocBuffer[4] = { 0 }; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; bool mediaTypeFound = false; bufferDesc = IOMemoryDescriptor::withAddress ( tocBuffer, sizeof ( tocBuffer ), kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); // Issue a READ_TOC_PMA_ATIP to find out if the media is // finalized or not if ( READ_TOC_PMA_ATIP ( request, bufferDesc, 0x00, 0x00, 0x00, sizeof ( tocBuffer ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } bufferDesc->release ( ); bufferDesc = NULL; if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { if ( fSupportedCDFeatures & kCDFeaturesWriteOnceMask ) { UInt8 discInfoBuffer[4]; bufferDesc = IOMemoryDescriptor::withAddress ( discInfoBuffer, sizeof ( discInfoBuffer ), kIODirectionIn ); require_nonzero ( bufferDesc, ReleaseTask ); if ( READ_DISC_INFORMATION ( request, bufferDesc, sizeof ( discInfoBuffer ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } bufferDesc->release ( ); bufferDesc = NULL; if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { switch ( discInfoBuffer[2] & kDiscStatusMask ) { case kDiscStatusEmpty: PANIC_NOW ( ( "A disc with a valid TOC should never be empty" ) ); break; case kDiscStatusOther: case kDiscStatusIncomplete: break; case kDiscStatusComplete: // Check to see if it is erasable. If so, flag it as CD-R/W media, // otherwise, consider it finalized. if ( discInfoBuffer[2] & kDiscStatusErasableMask ) { STATUS_LOG ( ( "fMediaType = CD-RW\n" ) ); fMediaType = kCDMediaTypeRW; } else { STATUS_LOG ( ( "fMediaType = CD-ROM\n" ) ); fMediaType = kCDMediaTypeROM; } mediaTypeFound = true; break; } } } else { // The drive is not a CD-R/W drive, so we mark the media as // finalized since it can't be written to. STATUS_LOG ( ( "fMediaType = CD-ROM\n" ) ); fMediaType = kCDMediaTypeROM; mediaTypeFound = true; } } if ( mediaTypeFound == false ) { if ( fSupportedCDFeatures & kCDFeaturesWriteOnceMask ) { UInt8 atipBuffer[kATIPBufferSize]; bufferDesc = IOMemoryDescriptor::withAddress ( atipBuffer, kATIPBufferSize, kIODirectionIn ); if ( READ_TOC_PMA_ATIP ( request, bufferDesc, 0x00, 0x04, 0x00, sizeof ( atipBuffer ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } bufferDesc->release ( ); bufferDesc = NULL; if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { UInt8 trackInfoBuffer[kTrackInfoBufferSize]; // Check the DiscType field in byte 7 of the READ_TOC_PMA_ATIP // format 0x04 to see if the disc is CD-RW or CD-R if ( atipBuffer[6] & kDiscTypeCDRWMask ) { STATUS_LOG ( ( "fMediaType = CD-RW\n" ) ); fMediaType = kCDMediaTypeRW; } else { STATUS_LOG ( ( "fMediaType = CD-R\n" ) ); fMediaType = kCDMediaTypeR; } bufferDesc = IOMemoryDescriptor::withAddress ( trackInfoBuffer, kTrackInfoBufferSize, kIODirectionIn ); // Check to see if the medium is blank if ( READ_TRACK_INFORMATION ( request, bufferDesc, 0x00, 0x01, kTrackInfoBufferSize, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } bufferDesc->release ( ); bufferDesc = NULL; if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { if ( trackInfoBuffer[6] & 0x40 ) { STATUS_LOG ( ( "media is blank\n" ) ); // Yes it's blank, make sure the blockCount is zero. fMediaBlockCount = 0; } } mediaTypeFound = true; } } } ReleaseTask: require_nonzero_quiet ( request, ReleaseDescriptor ); ReleaseSCSITask ( request ); request = NULL; ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return mediaTypeFound; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CheckWriteProtection - Determines write protect state of media. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::CheckWriteProtection ( void ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSITaskIdentifier request = NULL; IOMemoryDescriptor * bufferDesc = NULL; UInt8 buffer[16] = { 0 }; // Assume it is write protected fMediaIsWriteProtected = true; require_quiet ( ( fMediaType == kDVDMediaTypeRAM ), Exit ); require_quiet ( ( fSupportedDVDFeatures & kDVDFeaturesRandomWriteableMask ), Exit ); bufferDesc = IOMemoryDescriptor::withAddress ( buffer, sizeof ( buffer ), kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseDescriptor ); if ( GET_CONFIGURATION ( request, bufferDesc, 0x02, /* Only one feature descriptor */ kGetConfigurationProfileRandomWrite, sizeof ( buffer ), 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { // The current bit in the Random Writable Descriptor // tells us whether the disc is write protected. It is located // at byte 2 of the Random Writable Descriptor Feature page if ( buffer[kProfileFeatureHeaderSize + 2] & kRandomWritableProtectionMask ) { fMediaIsWriteProtected = false; } } ReleaseSCSITask ( request ); ReleaseDescriptor: require_nonzero_quiet ( bufferDesc, Exit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: Exit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IssueRead - Issues a synchronous read request. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::IssueRead ( IOMemoryDescriptor * buffer, UInt64 startBlock, UInt64 blockCount ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( READ_10 ( request, buffer, fMediaBlockSize, 0, 0, 0, ( SCSICmdField4Byte ) startBlock, ( SCSICmdField2Byte ) blockCount, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, fReadTimeoutDuration ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IssueRead - Issues a synchronous write request. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::IssueWrite ( IOMemoryDescriptor * buffer, UInt64 startBlock, UInt64 blockCount ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( WRITE_10 ( request, buffer, fMediaBlockSize, 0, 0, 0, ( SCSICmdField4Byte ) startBlock, ( SCSICmdField2Byte ) blockCount, 0 ) == true ) { // The command was successfully built, now send it serviceResponse = SendCommand ( request, fWriteTimeoutDuration ); } if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { status = kIOReturnSuccess; } else { status = kIOReturnIOError; } ReleaseSCSITask ( request ); ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IssueRead - Issues an asynchronous read request. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::IssueRead ( IOMemoryDescriptor * buffer, void * clientData, UInt64 startBlock, UInt64 blockCount ) { IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( READ_10 ( request, buffer, fMediaBlockSize, 0, 0, 0, startBlock, blockCount, 0 ) == true ) { SetApplicationLayerReference ( request, clientData ); // The command was successfully built, now send it SendCommand ( request, fReadTimeoutDuration, &IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete ); status = kIOReturnSuccess; } else { status = kIOReturnBadArgument; ReleaseSCSITask ( request ); request = NULL; } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IssueWrite - Issues an asynchronous write request. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSIMultimediaCommandsDevice::IssueWrite ( IOMemoryDescriptor * buffer, void * clientData, UInt64 startBlock, UInt64 blockCount ) { IOReturn status = kIOReturnNoResources; SCSITaskIdentifier request = NULL; request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); if ( WRITE_10 ( request, buffer, fMediaBlockSize, 0, 0, 0, ( SCSICmdField4Byte ) startBlock, ( SCSICmdField2Byte ) blockCount, 0 ) == true ) { SetApplicationLayerReference ( request, clientData ); // The command was successfully built, now send it SendCommand ( request, fWriteTimeoutDuration, &IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete ); status = kIOReturnSuccess; } else { status = kIOReturnBadArgument; ReleaseSCSITask ( request ); request = NULL; } ErrorExit: return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ConvertBCDToHex - Converts BCD values to Hex [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt8 IOSCSIMultimediaCommandsDevice::ConvertBCDToHex ( UInt8 binaryCodedDigit ) { UInt8 accumulator = 0; UInt8 x = 0; // Divide by 16 (equivalent to >> 4) x = ( binaryCodedDigit >> 4 ) & 0x0F; if ( x > 9 ) { return binaryCodedDigit; } accumulator = 10 * x; x = binaryCodedDigit & 0x0F; if ( x > 9 ) { return binaryCodedDigit; } accumulator += x; return accumulator; } #if 0 #pragma mark - #pragma mark ₯ Static Methods #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AsyncReadWriteComplete - Static completion routine for // read/write requests. [STATIC][PRIVATE] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete ( SCSITaskIdentifier request ) { IOReturn status = kIOReturnSuccess; UInt64 actCount = 0; IOSCSIMultimediaCommandsDevice * taskOwner = NULL; SCSITask * task = NULL; void * clientData = NULL; task = OSDynamicCast ( SCSITask, request ); require_nonzero ( task, ErrorExit ); taskOwner = OSDynamicCast ( IOSCSIMultimediaCommandsDevice, task->GetTaskOwner ( ) ); require_nonzero ( taskOwner, ErrorExit ); // Extract the client data from the SCSITask clientData = task->GetApplicationLayerReference ( ); require_nonzero ( clientData, ErrorExit ); if ( ( task->GetServiceResponse ( ) == kSCSIServiceResponse_TASK_COMPLETE ) && ( task->GetTaskStatus ( ) == kSCSITaskStatus_GOOD ) ) { // Our status is good, so return a success status = kIOReturnSuccess; actCount = task->GetRealizedDataTransferCount ( ); } else { // Set a generic IO error for starters status = kIOReturnIOError; // Either the task never completed or we have a status other than GOOD, // return an error. if ( task->GetTaskStatus ( ) == kSCSITaskStatus_CHECK_CONDITION ) { SCSI_Sense_Data senseDataBuffer; bool senseIsValid; senseIsValid = task->GetAutoSenseData ( &senseDataBuffer, sizeof ( senseDataBuffer ) ); if ( senseIsValid ) { IOLog ( "SAM Multimedia: READ or WRITE failed, ASC = 0x%02x, ASCQ = 0x%02x\n", senseDataBuffer.ADDITIONAL_SENSE_CODE, senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ); if ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE == 0x3A ) || ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE == 0x28 ) && ( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) ) { // Message up the chain that we do not have media taskOwner->messageClients ( kIOMessageMediaStateHasChanged, ( void * ) kIOMediaStateOffline ); taskOwner->ResetMediaCharacteristics ( ); taskOwner->fPollingMode = kPollingMode_NewMedia; taskOwner->EnablePolling ( ); } if ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE == 0x64 ) && ( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) { // The caller is trying to read blocks for which the block type // doesn't match. status = kIOReturnUnsupportedMode; } if ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE == 0x6F ) && ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x01 ) || ( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x02 ) || ( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x03 ) ) ) { // The key is no longer present for reading these // blocks->privileges error. status = kIOReturnNotPrivileged; } } } } if ( taskOwner->fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask ) { IODVDServices::AsyncReadWriteComplete ( clientData, status, actCount ); } else { IOCompactDiscServices::AsyncReadWriteComplete ( clientData, status, actCount ); } taskOwner->ReleaseSCSITask ( request ); ErrorExit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ sPollForMedia - Static routine to poll for media. [STATIC][PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSIMultimediaCommandsDevice::sPollForMedia ( void * pdtDriver, void * refCon ) { IOSCSIMultimediaCommandsDevice * driver; driver = ( IOSCSIMultimediaCommandsDevice * ) pdtDriver; driver->PollForMedia ( ); if ( driver->fPollingMode != kPollingMode_Suspended ) { // schedule the poller again driver->EnablePolling ( ); } // drop the retain associated with this poll driver->release ( ); } #if 0 #pragma mark - #pragma mark ₯ VTable Padding #pragma mark - #endif // Space reserved for future expansion. OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 1 ); /* ReadTOC */ OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 2 ); /* ReadDiscInfo */ OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 3 ); /* ReadTrackInfo */ OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 4 ); /* PowerDownHandler */ OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 5 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 6 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 7 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 8 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 9 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 10 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 11 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 12 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 13 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 14 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 15 ); OSMetaClassDefineReservedUnused ( IOSCSIMultimediaCommandsDevice, 16 );