IOSCSIMultimediaCommandsDevice.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/scsi-commands/SCSICommandDefinitions.h>
#include <IOKit/scsi-commands/SCSICmds_INQUIRY_Definitions.h>
#include "IOSCSIProtocolInterface.h"
#include "IOSCSIMultimediaCommandsDevice.h"
#include "IODVDServices.h"
#include "IOCompactDiscServices.h"
#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 );
#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
#define kMaxRetryCount 8
#define kMechanicalCapabilitiesModePageCode 0x2A
#define kCDAudioModePageCode 0x0E
#define kAppleKeySwitchProperty "AppleKeyswitch"
enum
{
kGetConfigurationProfileList = 0x0000,
kGetConfigurationProfileRandomWrite = 0x0020,
kGetConfigurationProfileIncrementalStreamedWrite = 0x0021,
kGetConfigurationProfileCDTAO = 0x002D,
kGetConfigurationProfileCDMastering = 0x002E,
kGetConfigurationProfileDVDWrite = 0x002F,
kGetConfigurationProfileAnalogAudio = 0x0103,
kGetConfigurationProfileDVDCSS = 0x0106
};
enum
{
kGetConfigurationCDROM_Feature = 0x0008,
kGetConfigurationCDR_Feature = 0x0009,
kGetConfigurationCDRW_Feature = 0x000A,
kGetConfigurationDVDROM_Feature = 0x0010,
kGetConfigurationDVDR_Feature = 0x0011,
kGetConfigurationDVDRAM_Feature = 0x0012, kGetConfigurationDVDRW_Feature = 0x0014
};
enum
{
kMechanicalCapabilitiesCDRMask = 0x01,
kMechanicalCapabilitiesCDRWMask = 0x02,
kMechanicalCapabilitiesTestWriteMask = 0x04,
kMechanicalCapabilitiesDVDROMMask = 0x08,
kMechanicalCapabilitiesDVDRMask = 0x10,
kMechanicalCapabilitiesDVDRAMMask = 0x20,
};
enum
{
kMechanicalCapabilitiesAnalogAudioMask = 0x01,
kMechanicalCapabilitiesCDDAStreamAccurateMask = 0x02,
kMechanicalCapabilitiesBUFMask = 0x80,
};
enum
{
kRandomWritableProtectionMask = 0x01
};
enum
{
kDiscTypeCDRWMask = 0x40
};
enum
{
kMediaCatalogValueFieldValidMask = 0x80,
kTrackCatalogValueFieldValidMask = 0x80
};
enum
{
kCDSpeed1x = 176,
kDVDSpeed1x = 1350
};
#define kAppleFeaturesModePageCode 0x31
struct AppleFeatures
{
UInt16 dataLength; UInt8 mediumType; UInt8 reserved[5]; UInt8 pageCode; UInt8 pageLength; UInt8 signature[4]; #if defined(__LITTLE_ENDIAN__)
UInt8 supportsLowPowerPoll:1; UInt8 reservedBits:7; #else
UInt8 reservedBits:7; UInt8 supportsLowPowerPoll:1; #endif
UInt8 reserved2; };
typedef struct AppleFeatures AppleFeatures;
#if 0
#pragma mark -
#pragma mark ₯ Public Methods - API Exported to layers above
#pragma mark -
#endif
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;
}
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;
}
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 )
{
( void ) SendCommand ( request, kThirtySecondTimeoutInMS );
}
}
else
{
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, kMediaStateUnlocked, 0 ) == true )
{
( void ) SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( START_STOP_UNIT ( request, 0, 0, 1, 0, 0 ) == true )
{
( void ) SendCommand ( request, kTenSecondTimeoutInMS );
}
ResetMediaCharacteristics ( );
messageClients ( kIOMessageTrayStateHasChanged, NULL, NULL );
if ( fLowPowerPollingEnabled == false )
{
fPollingMode = kPollingMode_NewMedia;
TicklePowerManager ( );
EnablePolling ( );
}
}
ReleaseSCSITask ( request );
status = kIOReturnSuccess;
ErrorExit:
return status;
}
IOReturn
IOSCSIMultimediaCommandsDevice::FormatMedia ( UInt64 byteCapacity )
{
IOReturn status = kIOReturnUnsupported;
return status;
}
UInt32
IOSCSIMultimediaCommandsDevice::GetFormatCapacities (
UInt64 * capacities,
UInt32 capacitiesMaxCount ) const
{
return 0;
}
IOReturn
IOSCSIMultimediaCommandsDevice::LockUnlockMedia ( bool doLock )
{
IOReturn status = kIOReturnSuccess;
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnNotAttached );
require_action ( IsProtocolAccessEnabled ( ), ErrorExit, status = kIOReturnOffline );
ErrorExit:
return status;
}
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 )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnInternalError;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportBlockSize ( UInt64 * blockSize )
{
*blockSize = fMediaBlockSize;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportEjectability ( bool * isEjectable )
{
*isEjectable = fMediaIsRemovable;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportLockability ( bool * isLockable )
{
*isLockable = true;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportPollRequirements (
bool * pollIsRequired,
bool * pollIsExpensive )
{
*pollIsRequired = false;
*pollIsExpensive = false;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportMaxReadTransfer (
UInt64 blockSize,
UInt64 * max )
{
UInt64 maxBlockCount = 256;
bool supported = false;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadBlockTransferCount,
&maxBlockCount );
if ( supported == false )
{
UInt64 maxByteCount = 0;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumReadTransferByteCount,
&maxByteCount );
if ( ( supported == true ) &&
( maxByteCount > 0 ) &&
( blockSize > 0 ) )
{
maxBlockCount = maxByteCount / blockSize;
}
}
*max = blockSize * maxBlockCount;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportMaxValidBlock ( UInt64 * maxBlock )
{
if ( fMediaBlockCount == 0 )
{
*maxBlock = 0;
}
else
{
*maxBlock = fMediaBlockCount - 1;
}
STATUS_LOG ( ( "%s::%s maxBlockHi = 0x%x, maxBlockLo = 0x%x\n", getName ( ),
__FUNCTION__, ( *maxBlock ), ( *maxBlock ) >> 32 ) );
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportMaxWriteTransfer ( UInt64 blockSize,
UInt64 * max )
{
UInt64 maxBlockCount = 256;
bool supported;
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteBlockTransferCount,
&maxBlockCount );
if ( supported == false )
{
UInt64 maxByteCount = 0;
supported = GetProtocolDriver ( )->IsProtocolServiceSupported (
kSCSIProtocolFeature_MaximumWriteTransferByteCount,
&maxByteCount );
if ( ( supported == true ) &&
( maxByteCount > 0 ) &&
( blockSize > 0 ) )
{
maxBlockCount = maxByteCount / blockSize;
}
}
*max = blockSize * maxBlockCount;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportMediaState (
bool * mediaPresent,
bool * changed )
{
*mediaPresent = fMediaPresent;
*changed = fMediaChanged;
if ( fMediaChanged )
{
fMediaChanged = !fMediaChanged;
}
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportRemovability ( bool * isRemovable )
{
*isRemovable = fMediaIsRemovable;
return kIOReturnSuccess;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReportWriteProtection (
bool * isWriteProtected )
{
*isWriteProtected = fMediaIsWriteProtected;
return kIOReturnSuccess;
}
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,
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
{
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;
}
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 );
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 ( kIOMessageTrayStateHasChanged, NULL, NULL );
ErrorExit:
return status;
}
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 & ~kCDSectorAreaSubChannel ) >> 7 ) & 0x1,
( ( sectorArea & ~kCDSectorAreaSubChannel ) >> 5 ) & 0x3,
( ( sectorArea & ~kCDSectorAreaSubChannel ) >> 4 ) & 0x1,
( ( sectorArea & ~kCDSectorAreaSubChannel ) >> 3 ) & 0x1,
( ( sectorArea & ~kCDSectorAreaSubChannel ) >> 1 ) & 0x3,
sectorArea & kCDSectorAreaSubChannel,
0 ) == true )
{
SetApplicationLayerReference ( request, clientData );
SendCommand ( request, 0, &IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
ErrorExit:
return status;
}
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 )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
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;
}
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 )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
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;
}
IOReturn
IOSCSIMultimediaCommandsDevice::ReadTOC ( IOMemoryDescriptor * buffer )
{
return ReadTOC ( buffer, kCDTOCFormatTOC, 0x01, 0x00, NULL );
}
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 ( 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 ( ) );
}
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
{
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, format,
0,
bufferToUse->getLength ( ),
0 ) == true )
{
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;
ptr = ( UInt8 * ) bufferToUse->getVirtualSegment ( 0, &bufLength );
sizeOfTOC = OSReadBigInt16 ( ptr, 0 ) + sizeof ( UInt16 );
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 ) ), ReleaseDescriptor, status = kIOReturnError );
lastSessionNum = ptr[3];
for ( index = 4; index < ( UInt32 ) ( sizeOfTOC - 4 ); index += 11 )
{
if ( ptr[index] != lastSessionNum )
{
continue;
}
if ( ptr[index + 3] != 0xA2 )
{
continue;
}
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" ) );
ptr[2] = ConvertBCDToHex ( ptr[2] );
ptr[3] = ConvertBCDToHex ( ptr[3] );
ptr = &ptr[4];
for ( index = 0; index < ( UInt32 ) ( sizeOfTOC - 4 ); index += 11 )
{
if ( ( ptr[index + 3] == 0xA0 ) || ( ptr[index + 3] == 0xA1 ) )
{
ptr[index + 8] = ConvertBCDToHex ( ptr[index + 8] );
}
else
{
ptr[index + 3] = ConvertBCDToHex ( ptr[index + 3] );
ptr[index + 8] = ConvertBCDToHex ( ptr[index + 8] );
ptr[index + 9] = ConvertBCDToHex ( ptr[index + 9] );
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 ) ), ErrorExit, status = kIOReturnError );
}
}
}
}
else
{
if ( actualByteCount != NULL )
{
*actualByteCount = 0;
}
status = kIOReturnIOError;
}
ReleaseDescriptor:
require_nonzero_quiet ( doubleBuffer, ReleaseTask );
doubleBuffer->release ( );
doubleBuffer = NULL;
ReleaseTask:
require_nonzero_quiet ( request, ErrorExit );
ReleaseSCSITask ( request );
request = NULL;
ErrorExit:
STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::ReadTOC status = %d\n", status ) );
return status;
}
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 )
{
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;
}
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 )
{
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;
}
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 )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
Exit:
return status;
}
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 );
STARTING_MSF = ( timeStart.minute << 24 ) |
( timeStart.second << 16 ) |
( timeStart.frame << 8 );
ENDING_MSF = ( timeStop.minute << 24 ) |
( timeStop.second << 16 ) |
( timeStop.frame << 8 );
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 )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
Exit:
return status;
}
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 );
SCAN_STARTING_ADDRESS_FIELD = ( timeStart.minute << 24 ) |
( timeStart.second << 16 ) |
( timeStart.frame << 8 );
OSWriteBigInt32 ( &SCAN_STARTING_ADDRESS_FIELD, 0, SCAN_STARTING_ADDRESS_FIELD );
if ( SCAN ( request,
reverse,
0,
SCAN_STARTING_ADDRESS_FIELD,
0x01,
0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
Exit:
return status;
}
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 )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
Exit:
return status;
}
IOReturn
IOSCSIMultimediaCommandsDevice::GetAudioStatus ( CDAudioStatus * status )
{
return kIOReturnUnsupported;
}
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;
}
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 )
{
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;
}
UInt32
IOSCSIMultimediaCommandsDevice::GetMediaType ( void )
{
return fMediaType;
}
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 )
{
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;
}
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 )
{
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;
}
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 )
{
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;
}
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 )
{
blockDescriptorLength = OSReadBigInt16 ( mechanicalCapabilities, 6 );
headerSize = kModeSense10ParameterHeaderSize;
}
else
{
blockDescriptorLength = mechanicalCapabilities[3];
headerSize = kModeSense6ParameterHeaderSize;
}
minBufferSize = headerSize + blockDescriptorLength + 16;
require_action ( ( actualSize >= minBufferSize ),
ReleaseDescriptor,
status = kIOReturnInternalError );
pageCode = mechanicalCapabilities[headerSize + blockDescriptorLength] & 0x3F;
require_action ( ( pageCode == kMechanicalCapabilitiesModePageCode ),
ReleaseDescriptor,
status = kIOReturnInternalError );
*kilobytesPerSecond = OSReadBigInt16 (
mechanicalCapabilities,
headerSize + blockDescriptorLength + 16 );
status = kIOReturnSuccess;
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return status;
}
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
bool
IOSCSIMultimediaCommandsDevice::InitializeDeviceSupport ( void )
{
bool setupSuccessful = false;
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 =
( IOSCSIMultimediaCommandsDeviceExpansionData * ) IOMalloc (
sizeof ( IOSCSIMultimediaCommandsDeviceExpansionData ) );
require_nonzero ( fIOSCSIMultimediaCommandsDeviceReserved, ErrorExit );
bzero ( fIOSCSIMultimediaCommandsDeviceReserved,
sizeof ( IOSCSIMultimediaCommandsDeviceExpansionData ) );
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 ) );
return setupSuccessful;
ReleaseExpansionData:
require_nonzero_quiet ( fIOSCSIMultimediaCommandsDeviceReserved, ErrorExit );
IOFree ( fIOSCSIMultimediaCommandsDeviceReserved,
sizeof ( IOSCSIMultimediaCommandsDeviceExpansionData ) );
fIOSCSIMultimediaCommandsDeviceReserved = NULL;
ErrorExit:
return setupSuccessful;
}
void
IOSCSIMultimediaCommandsDevice::StartDeviceSupport ( void )
{
OSBoolean * shouldNotPoll = NULL;
shouldNotPoll = OSDynamicCast (
OSBoolean,
getProperty ( kAppleKeySwitchProperty ) );
if ( shouldNotPoll != NULL )
{
require ( shouldNotPoll->isFalse ( ), Exit );
}
EnablePolling ( );
Exit:
CreateStorageServiceNub ( );
}
void
IOSCSIMultimediaCommandsDevice::SuspendDeviceSupport ( void )
{
if ( fPollingMode != kPollingMode_Suspended )
{
DisablePolling ( );
}
}
void
IOSCSIMultimediaCommandsDevice::ResumeDeviceSupport ( void )
{
if ( fMediaPresent == false )
{
fPollingMode = kPollingMode_NewMedia;
EnablePolling ( );
}
}
void
IOSCSIMultimediaCommandsDevice::StopDeviceSupport ( void )
{
DisablePolling ( );
}
void
IOSCSIMultimediaCommandsDevice::TerminateDeviceSupport ( void )
{
STATUS_LOG ( ( "IOSCSIMultimediaCommandsDevice::cleanUp called.\n" ) );
if ( fPollingThread != NULL )
{
thread_call_free ( fPollingThread );
fPollingThread = NULL;
}
if ( fPowerDownNotifier != NULL )
{
fPowerDownNotifier->remove ( );
fPowerDownNotifier = NULL;
}
}
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 ( ) );
result = true;
ErrorExit:
return result;
}
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;
}
if ( fIOSCSIMultimediaCommandsDeviceReserved != NULL )
{
IOFree ( fIOSCSIMultimediaCommandsDeviceReserved,
sizeof ( IOSCSIMultimediaCommandsDeviceExpansionData ) );
fIOSCSIMultimediaCommandsDeviceReserved = NULL;
}
}
SCSIMultimediaCommands *
IOSCSIMultimediaCommandsDevice::GetSCSIMultimediaCommandObject ( void )
{
check ( fSCSIMultimediaCommandObject );
return fSCSIMultimediaCommandObject;
}
SCSIBlockCommands *
IOSCSIMultimediaCommandsDevice::GetSCSIBlockCommandObject ( void )
{
check ( fSCSIBlockCommandObject );
return fSCSIBlockCommandObject;
}
SCSIPrimaryCommands *
IOSCSIMultimediaCommandsDevice::GetSCSIPrimaryCommandObject ( void )
{
check ( fSCSIMultimediaCommandObject );
return OSDynamicCast ( SCSIPrimaryCommands,
GetSCSIMultimediaCommandObject ( ) );
}
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;
}
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 )
{
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 )
{
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 ) )
{
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
{
IOSleep ( 200 );
}
} while ( ( driveReady == false ) && ( isInactive ( ) == false ) );
bufferDesc->release ( );
ReleaseSCSITask ( request );
result = isInactive ( ) ? false : true;
return result;
}
void
IOSCSIMultimediaCommandsDevice::EnablePolling ( void )
{
AbsoluteTime time;
require ( ( isInactive ( ) == false ) && fPollingThread, Exit );
require ( ( fPollingMode != kPollingMode_Suspended ), Exit );
require_nonzero ( fPollingThread, Exit );
retain ( );
clock_interval_to_deadline ( 1000, kMillisecondScale, &time );
thread_call_enter_delayed ( fPollingThread, time );
Exit:
return;
}
void
IOSCSIMultimediaCommandsDevice::DisablePolling ( void )
{
fPollingMode = kPollingMode_Suspended;
require ( thread_call_cancel ( fPollingThread ), Exit );
release ( );
Exit:
return;
}
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, NULL, NULL );
}
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 )
{
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;
status = parent->messageClient ( kIOMessageServiceIsRequestingClose, ( IOBlockStorageDriver * ) childService );
if ( status == kIOReturnSuccess )
{
message ( kSCSIServicesNotification_Suspend, NULL, NULL );
ResetMediaCharacteristics ( );
messageClients ( kSCSIServicesNotification_ExclusivityChanged, NULL, NULL );
}
else
{
ERROR_LOG ( ( "BlockStorageDriver wouldn't close, status = %d\n", status ) );
super::HandleSetUserClientExclusivityState ( userClient, !state );
}
break;
}
parent->release ( );
}
}
if ( childList != NULL )
childList->release ( );
}
}
else
{
message ( kSCSIServicesNotification_Suspend, NULL, NULL );
status = kIOReturnSuccess;
}
}
ErrorExit:
ERROR_LOG ( ( "IOSCSIMultimediaCommandsDevice::HandleSetUserClientExclusivityState status = %d\n", status ) );
return status;
}
void
IOSCSIMultimediaCommandsDevice::CreateStorageServiceNub ( void )
{
IOService * nub = NULL;
if ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask )
{
nub = new IODVDServices;
}
else
{
nub = new 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;
}
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;
}
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 )
{
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;
}
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;
}
IOReturn
IOSCSIMultimediaCommandsDevice::DetermineDeviceFeatures ( void )
{
IOReturn status = kIOReturnSuccess;
status = GetDeviceConfiguration ( );
check ( status == kIOReturnSuccess );
status = GetMechanicalCapabilities ( );
check ( status == kIOReturnSuccess );
if ( status != kIOReturnSuccess )
{
STATUS_LOG ( ( "Device supports CD-ROM\n" ) );
status = kIOReturnSuccess;
fSupportedCDFeatures |= kCDFeaturesReadStructuresMask;
}
CheckPowerConditionsModePage ( );
( void ) CheckForLowPowerPollingSupport ( );
setProperty ( kIOPropertySupportedCDFeatures, fSupportedCDFeatures, 32 );
setProperty ( kIOPropertySupportedDVDFeatures, fSupportedDVDFeatures, 32 );
fDeviceCharacteristicsDictionary->setObject (
kIOPropertySupportedCDFeatures,
getProperty ( kIOPropertySupportedCDFeatures ) );
fDeviceCharacteristicsDictionary->setObject (
kIOPropertySupportedDVDFeatures,
getProperty ( kIOPropertySupportedDVDFeatures ) );
return status;
}
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,
kGetConfigurationProfileList,
kProfileDataLengthFieldSize,
0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
*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;
}
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 );
numProfiles = ( actualProfileSize - kProfileFeatureHeaderSize -
kProfileDescriptorSize ) / kProfileDescriptorSize;
require_nonzero_action ( numProfiles,
ErrorExit,
status = kIOReturnIOError );
STATUS_LOG ( ( "numProfiles = %d\n", numProfiles ) );
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,
kGetConfigurationProfileList,
actualProfileSize,
0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = ParseFeatureList ( numProfiles,
profilePtr +
kProfileFeatureHeaderSize +
kProfileDescriptorSize );
require_success ( status, ReleaseTask );
}
if ( GET_CONFIGURATION ( request,
bufferDesc,
0x02,
kGetConfigurationProfileAnalogAudio,
4,
0 ) == true )
{
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,
kGetConfigurationProfileIncrementalStreamedWrite,
4,
0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
STATUS_LOG ( ( "Device supports Packet Writing \n" ) );
fSupportedCDFeatures |= kCDFeaturesPacketWriteMask;
}
if ( GET_CONFIGURATION ( request,
bufferDesc,
0x02,
kGetConfigurationProfileCDTAO,
8,
0 ) == true )
{
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;
}
}
if ( GET_CONFIGURATION ( request,
bufferDesc,
0x02,
kGetConfigurationProfileCDMastering,
8,
0 ) == true )
{
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;
}
}
if ( ( fSupportedDVDFeatures & ( kDVDFeaturesWriteOnceMask |
kDVDFeaturesRandomWriteableMask |
kDVDFeaturesReWriteableMask ) ) )
{
if ( GET_CONFIGURATION ( request,
bufferDesc,
0x02,
kGetConfigurationProfileDVDWrite,
8,
0 ) == true )
{
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;
}
}
}
if ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask )
{
if ( GET_CONFIGURATION ( request,
bufferDesc,
0x02,
kGetConfigurationProfileDVDCSS,
4,
0 ) == true )
{
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;
}
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;
default:
STATUS_LOG ( ( "%s::%s unknown drive type\n", getName ( ), __FUNCTION__ ) );
break;
}
profilePtr += kProfileDescriptorSize;
}
status = kIOReturnSuccess;
ErrorExit:
return status;
}
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;
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;
}
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 )
{
blockDescriptorLength = OSReadBigInt16 ( mechanicalCapabilities, 6 );
headerSize = kModeSense10ParameterHeaderSize;
}
else
{
blockDescriptorLength = mechanicalCapabilities[3];
headerSize = kModeSense6ParameterHeaderSize;
}
minBufferSize = headerSize +
blockDescriptorLength +
2 +
kMechanicalCapabilitiesMinBufferSize;
require_action ( ( actualSize >= minBufferSize ),
ReleaseDescriptor,
status = kIOReturnInternalError );
pageCode = mechanicalCapabilities[headerSize + blockDescriptorLength] & 0x3F;
require_action ( ( pageCode == kMechanicalCapabilitiesModePageCode ),
ReleaseDescriptor,
status = kIOReturnInternalError );
status = ParseMechanicalCapabilities ( mechanicalCapabilities +
headerSize +
blockDescriptorLength +
2 );
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, ErrorExit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
return status;
}
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 );
}
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;
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;
}
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;
}
mechanicalCapabilities++;
if ( ( *mechanicalCapabilities & kMechanicalCapabilitiesCDDAStreamAccurateMask ) != 0 )
{
STATUS_LOG ( ( "Device supports CD-DA stream accurate reads\n" ) );
fSupportedCDFeatures |= kCDFeaturesCDDAStreamAccurateMask;
}
STATUS_LOG ( ( "Device supports CD-ROM\n" ) );
fSupportedCDFeatures |= kCDFeaturesReadStructuresMask;
ErrorExit:
return status;
}
void
IOSCSIMultimediaCommandsDevice::SetMediaCharacteristics (
UInt32 blockSize,
UInt32 blockCount )
{
fMediaBlockSize = blockSize;
fMediaBlockCount = blockCount;
}
void
IOSCSIMultimediaCommandsDevice::ResetMediaCharacteristics ( void )
{
fMediaBlockSize = 0;
fMediaBlockCount = 0;
fCurrentDiscSpeed = 0;
fMediaPresent = false;
fMediaType = kCDMediaTypeUnknown;
fMediaIsWriteProtected = true;
}
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 );
if ( TEST_UNIT_READY ( request, 0 ) == true )
{
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 );
if ( REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ) == true )
{
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:
if ( ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) ||
( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3A ) )
{
break;
}
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x01 ) )
{
messageClients ( kIOMessageMediaAccessChange, ( void * ) kMessageDeterminingMediaPresence, NULL );
break;
}
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x57 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
break;
}
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x04 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x04 ) )
{
break;
}
shouldEjectMedia = true;
break;
case kSENSE_KEY_MEDIUM_ERROR:
case kSENSE_KEY_HARDWARE_ERROR:
shouldEjectMedia = true;
break;
case kSENSE_KEY_BLANK_CHECK:
if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x64 ) &&
( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
break;
}
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 ) );
EjectTheMedia ( );
}
require_quiet ( mediaFound, ReleaseTask );
messageClients ( kIOMessageMediaAccessChange, ( void * ) kMessageFoundMedia, NULL );
if ( fMediaIsRemovable == true )
{
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, 1, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
}
bufferDesc = IOMemoryDescriptor::withAddress ( capacityData,
8,
kIODirectionIn );
require_nonzero ( bufferDesc, ReleaseTask );
if ( READ_CAPACITY ( request,
bufferDesc,
0,
0x00,
0,
0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
if ( capacityData[0] == 0 )
{
SetMediaCharacteristics ( capacityData[1], capacityData[0] );
}
else
{
SetMediaCharacteristics ( OSSwapBigToHostInt32 ( capacityData[1] ),
OSSwapBigToHostInt32 ( capacityData[0] ) + 1 );
}
STATUS_LOG ( ( "%s: Media capacity: 0x%x and block size: 0x%x\n",
getName ( ), fMediaBlockCount, fMediaBlockSize ) );
}
else
{
ERROR_LOG ( ( "%s: Read Capacity failed\n", getName ( ) ) );
if ( fMediaIsRemovable == true )
{
if ( PREVENT_ALLOW_MEDIUM_REMOVAL ( request, 0, 0 ) == true )
{
serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS );
}
}
goto ReleaseTask;
}
DetermineMediaType ( );
CheckWriteProtection ( );
fMediaPresent = true;
fMediaChanged = true;
fPollingMode = kPollingMode_Suspended;
messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOnline,
0 );
ReleaseTask:
require_nonzero_quiet ( request, ReleaseDescriptor );
ReleaseSCSITask ( request );
request = NULL;
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, Exit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
Exit:
return;
}
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 );
if ( ( OSReadBigInt32 ( &appleFeaturesBuffer.signature, 0 ) == '.App' ) &&
( appleFeaturesBuffer.supportsLowPowerPoll ) )
{
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 ) ) )
{
fDeviceSupportsPowerConditions = false;
fDeviceSupportsLowPowerPolling = false;
}
if ( fSupportedDVDFeatures == 0 )
{
fDeviceSupportsPowerConditions = false;
}
boolValue = OSBoolean::withBoolean ( fDeviceSupportsLowPowerPolling );
if ( boolValue != NULL )
{
fDeviceCharacteristicsDictionary->setObject ( kIOPropertyLowPowerPolling, boolValue );
boolValue->release ( );
boolValue = NULL;
}
ErrorExit:
return status;
}
void
IOSCSIMultimediaCommandsDevice::DetermineMediaType ( void )
{
bool mediaTypeFound = false;
mediaTypeFound = CheckForDVDMediaType ( );
if ( mediaTypeFound == false )
{
mediaTypeFound = CheckForCDMediaType ( );
}
messageClients ( kIOMessageMediaAccessChange,
( void * ) kMessageMediaTypeDetermined,
NULL );
SetMediaAccessSpeed ( 0xFFFF );
}
bool
IOSCSIMultimediaCommandsDevice::CheckForDVDMediaType ( void )
{
SCSITaskIdentifier request = NULL;
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
IOMemoryDescriptor * bufferDesc = NULL;
bool mediaTypeFound = false;
DVDPhysicalFormatInfo physicalFormatInfo;
bzero ( &physicalFormatInfo, sizeof ( DVDPhysicalFormatInfo ) );
require_quiet ( ( fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask ), Exit );
bufferDesc = IOMemoryDescriptor::withAddress (
( void * ) &physicalFormatInfo,
kDVDPhysicalFormatInfoBufferSize,
kIODirectionIn );
require_nonzero ( bufferDesc, Exit );
request = GetSCSITask ( );
require_nonzero ( request, ReleaseDescriptor );
if ( READ_DVD_STRUCTURE ( request,
bufferDesc,
0x00,
0x00,
0x00,
kDVDPhysicalFormatInfoBufferSize,
0x00,
0x00 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
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 9:
STATUS_LOG ( ( "fMediaType = DVD+RW\n" ) );
fMediaType = kDVDMediaTypePlusRW;
break;
}
}
if ( fMediaType != kCDMediaTypeUnknown )
{
mediaTypeFound = true;
}
ReleaseSCSITask ( request );
ReleaseDescriptor:
require_nonzero ( bufferDesc, Exit );
bufferDesc->release ( );
bufferDesc = NULL;
Exit:
return mediaTypeFound;
}
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 );
if ( READ_TOC_PMA_ATIP ( request,
bufferDesc,
0x00,
0x00,
0x00,
sizeof ( tocBuffer ),
0 ) == true )
{
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 )
{
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:
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
{
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 )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
bufferDesc->release ( );
bufferDesc = NULL;
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
UInt8 trackInfoBuffer[kTrackInfoBufferSize];
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 );
if ( READ_TRACK_INFORMATION ( request,
bufferDesc,
0x00,
0x01,
kTrackInfoBufferSize,
0 ) == true )
{
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" ) );
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;
}
void
IOSCSIMultimediaCommandsDevice::CheckWriteProtection ( void )
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskIdentifier request = NULL;
IOMemoryDescriptor * bufferDesc = NULL;
UInt8 buffer[16] = { 0 };
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,
kGetConfigurationProfileRandomWrite,
sizeof ( buffer ),
0 ) == true )
{
serviceResponse = SendCommand ( request, kThirtySecondTimeoutInMS );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
if ( buffer[kProfileFeatureHeaderSize + 2] & kRandomWritableProtectionMask )
{
fMediaIsWriteProtected = false;
}
}
ReleaseSCSITask ( request );
ReleaseDescriptor:
require_nonzero_quiet ( bufferDesc, Exit );
bufferDesc->release ( );
bufferDesc = NULL;
ErrorExit:
Exit:
return;
}
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 )
{
serviceResponse = SendCommand ( request, 0 );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
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 )
{
serviceResponse = SendCommand ( request, 0 );
}
if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) &&
( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnIOError;
}
ReleaseSCSITask ( request );
ErrorExit:
return status;
}
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 );
SendCommand ( request,
0,
&IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
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 );
SendCommand ( request,
0,
&IOSCSIMultimediaCommandsDevice::AsyncReadWriteComplete );
status = kIOReturnSuccess;
}
else
{
status = kIOReturnBadArgument;
}
ErrorExit:
return status;
}
UInt8
IOSCSIMultimediaCommandsDevice::ConvertBCDToHex ( UInt8 binaryCodedDigit )
{
UInt8 accumulator = 0;
UInt8 x = 0;
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
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 );
clientData = task->GetApplicationLayerReference ( );
require_nonzero ( clientData, ErrorExit );
if ( ( task->GetServiceResponse ( ) == kSCSIServiceResponse_TASK_COMPLETE ) &&
( task->GetTaskStatus ( ) == kSCSITaskStatus_GOOD ) )
{
status = kIOReturnSuccess;
actCount = task->GetRealizedDataTransferCount ( );
}
else
{
status = kIOReturnIOError;
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 ) ) )
{
taskOwner->messageClients ( kIOMessageMediaStateHasChanged,
( void * ) kIOMediaStateOffline,
0 );
taskOwner->ResetMediaCharacteristics ( );
taskOwner->EnablePolling ( );
}
if ( ( senseDataBuffer.ADDITIONAL_SENSE_CODE == 0x64 ) &&
( senseDataBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) )
{
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 ) ) )
{
status = kIOReturnNotPrivileged;
}
}
}
}
taskOwner->ReleaseSCSITask ( request );
if ( taskOwner->fSupportedDVDFeatures & kDVDFeaturesReadStructuresMask )
{
IODVDServices::AsyncReadWriteComplete ( clientData, status, actCount );
}
else
{
IOCompactDiscServices::AsyncReadWriteComplete ( clientData, status, actCount );
}
ErrorExit:
return;
}
void
IOSCSIMultimediaCommandsDevice::sPollForMedia (
void * pdtDriver,
void * refCon )
{
IOSCSIMultimediaCommandsDevice * driver;
driver = ( IOSCSIMultimediaCommandsDevice * ) pdtDriver;
driver->PollForMedia ( );
if ( driver->fPollingMode != kPollingMode_Suspended )
{
driver->EnablePolling ( );
}
driver->release ( );
}
#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif
OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 1 );
OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 2 );
OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 3 );
OSMetaClassDefineReservedUsed ( IOSCSIMultimediaCommandsDevice, 4 );
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 );