#include <libkern/c++/OSString.h>
#include <libkern/c++/OSDictionary.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include "SCSITaskLib.h"
#include "SCSITaskLibPriv.h"
#include "IOSCSIProtocolInterface.h"
#include "IOSCSIPeripheralDeviceType05.h"
#include "IODVDServices.h"
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "DVD Services"
#if DEBUG
#define SCSI_DVD_SERVICES_DEBUGGING_LEVEL 0
#endif
#include "IOSCSIArchitectureModelFamilyDebugging.h"
#if ( SCSI_DVD_SERVICES_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( SCSI_DVD_SERVICES_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( SCSI_DVD_SERVICES_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define super IODVDBlockStorageDevice
OSDefineMetaClassAndStructors ( IODVDServices, IODVDBlockStorageDevice );
#define kNumberRetries 4
struct BlockServicesClientData
{
IODVDServices * owner;
IOStorageCompletion completionData;
IOMemoryDescriptor * clientBuffer;
UInt32 clientStartingBlock;
UInt32 clientRequestedBlockCount;
bool clientReadCDCall;
CDSectorArea clientSectorArea;
CDSectorType clientSectorType;
UInt32 retriesLeft;
};
typedef struct BlockServicesClientData BlockServicesClientData;
#if 0
#pragma mark -
#pragma mark ₯ Public Methods - API Exported to layers above
#pragma mark -
#endif
bool
IODVDServices::start ( IOService * provider )
{
OSNumber * cdFeatures = NULL;
OSNumber * dvdFeatures = NULL;
UInt32 cdFeaturesFlags = 0;
UInt32 dvdFeaturesFlags = 0;
bool result = false;
fProvider = OSDynamicCast ( IOSCSIPeripheralDeviceType05, provider );
require_nonzero ( fProvider, ErrorExit );
require ( super::start ( fProvider ), ErrorExit );
cdFeatures = ( OSNumber * ) fProvider->getProperty (
kIOPropertySupportedCDFeatures );
dvdFeatures = ( OSNumber * ) fProvider->getProperty (
kIOPropertySupportedDVDFeatures );
check ( cdFeatures );
check ( dvdFeatures );
cdFeaturesFlags = ( kCDFeaturesWriteOnceMask | kCDFeaturesReWriteableMask ) &
cdFeatures->unsigned32BitValue ( );
dvdFeaturesFlags = ( kDVDFeaturesWriteOnceMask | kDVDFeaturesReWriteableMask |
kDVDFeaturesRandomWriteableMask ) &
dvdFeatures->unsigned32BitValue ( );
if ( ( cdFeaturesFlags != 0 ) || ( dvdFeaturesFlags != 0 ) )
{
require ( setProperty ( kIOMatchCategoryKey,
kSCSITaskUserClientIniterKey ), ErrorExit );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey,
fProvider->GetProtocolCharacteristicsDictionary ( ) );
setProperty ( kIOPropertyDeviceCharacteristicsKey,
fProvider->GetDeviceCharacteristicsDictionary ( ) );
registerService ( );
result = true;
ErrorExit:
return result;
}
bool
IODVDServices::open ( IOService * client,
IOOptionBits options,
IOStorageAccess access )
{
return super::open ( client, options, ( void * ) access );
}
IOReturn
IODVDServices::message ( UInt32 type,
IOService * nub,
void * arg )
{
IOReturn status = kIOReturnSuccess;
switch ( type )
{
case kSCSIServicesNotification_ExclusivityChanged:
case kIOMessageMediaStateHasChanged:
case kIOMessageTrayStateChange:
case kIOMessageMediaAccessChange:
{
status = messageClients ( type, arg );
}
break;
default:
{
status = super::message ( type, nub, arg );
}
break;
}
return status;
}
IOReturn
IODVDServices::setProperties ( OSObject * properties )
{
IOReturn status = kIOReturnBadArgument;
OSDictionary * dict = NULL;
UInt8 trayState = 0xFF;
Boolean userClientActive = false;
require_nonzero ( properties, ErrorExit );
dict = OSDynamicCast ( OSDictionary, properties );
require_nonzero ( dict, ErrorExit );
require_nonzero_action ( fProvider,
ErrorExit,
status = kIOReturnNotAttached );
fProvider->retain ( );
require_nonzero ( dict->getObject ( "TrayState" ), ReleaseProvider );
userClientActive = fProvider->GetUserClientExclusivityState ( );
require_action (
( userClientActive == false ),
ReleaseProvider,
status = kIOReturnExclusiveAccess;
messageClients ( kIOMessageTrayStateChange,
( void * ) kMessageTrayStateChangeRequestRejected ) );
fProvider->CheckPowerState ( );
status = fProvider->GetTrayState ( &trayState );
require_success ( status, ReleaseProvider );
status = fProvider->SetTrayState ( !trayState );
ReleaseProvider:
fProvider->release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doAsyncReadCD ( IOMemoryDescriptor * buffer,
UInt32 block,
UInt32 nblks,
CDSectorArea sectorArea,
CDSectorType sectorType,
IOStorageCompletion completion )
{
BlockServicesClientData * clientData = NULL;
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
clientData = IONew ( BlockServicesClientData, 1 );
require_nonzero_action ( clientData, ErrorExit, status = kIOReturnNoResources );
retain ( );
fProvider->retain ( );
clientData->owner = this;
clientData->completionData = completion;
clientData->clientBuffer = buffer;
clientData->clientStartingBlock = block;
clientData->clientRequestedBlockCount = nblks;
clientData->clientReadCDCall = true;
clientData->clientSectorArea = sectorArea;
clientData->clientSectorType = sectorType;
clientData->retriesLeft = kNumberRetries;
fProvider->CheckPowerState ( );
status = fProvider->AsyncReadCD ( buffer,
block,
nblks,
sectorArea,
sectorType,
( void * ) clientData );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doAsyncReadWrite ( IOMemoryDescriptor * buffer,
UInt32 block,
UInt32 nblks,
IOStorageCompletion completion )
{
BlockServicesClientData * clientData = NULL;
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
clientData = IONew ( BlockServicesClientData, 1 );
require_nonzero_action ( clientData, ErrorExit, status = kIOReturnNoResources );
retain ( );
fProvider->retain ( );
clientData->owner = this;
clientData->completionData = completion;
clientData->clientBuffer = buffer;
clientData->clientStartingBlock = block;
clientData->clientRequestedBlockCount = nblks;
clientData->clientReadCDCall = false;
clientData->retriesLeft = kNumberRetries;
fProvider->CheckPowerState ( );
status = fProvider->AsyncReadWrite ( buffer, block, nblks, ( void * ) clientData );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doFormatMedia ( UInt64 byteCapacity )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->FormatMedia ( byteCapacity );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
UInt32
IODVDServices::doGetFormatCapacities ( UInt64 * capacities,
UInt32 capacitiesMaxCount ) const
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->GetFormatCapacities ( capacities, capacitiesMaxCount );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doEjectMedia ( void )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->EjectTheMedia ( );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doLockUnlockMedia ( bool doLock )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->LockUnlockMedia ( doLock );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
char *
IODVDServices::getVendorString ( void )
{
return fProvider->GetVendorString ( );
}
char *
IODVDServices::getProductString ( void )
{
return fProvider->GetProductString ( );
}
char *
IODVDServices::getRevisionString ( void )
{
return fProvider->GetRevisionString ( );
}
char *
IODVDServices::getAdditionalDeviceInfoString ( void )
{
STATUS_LOG ( ( "%s::%s called\n", getName ( ), __FUNCTION__ ) );
return ( "No Additional Device Info" );
}
IOReturn
IODVDServices::reportBlockSize ( UInt64 * blockSize )
{
return fProvider->ReportBlockSize ( blockSize );
}
IOReturn
IODVDServices::reportEjectability ( bool * isEjectable )
{
return fProvider->ReportEjectability ( isEjectable );
}
IOReturn
IODVDServices::reportLockability ( bool * isLockable )
{
return fProvider->ReportLockability ( isLockable );
}
IOReturn
IODVDServices::reportMediaState ( bool * mediaPresent,
bool * changed )
{
return fProvider->ReportMediaState ( mediaPresent, changed );
}
IOReturn
IODVDServices::reportPollRequirements ( bool * pollIsRequired,
bool * pollIsExpensive )
{
return fProvider->ReportPollRequirements ( pollIsRequired, pollIsExpensive );
}
IOReturn
IODVDServices::reportMaxReadTransfer ( UInt64 blockSize,
UInt64 * max )
{
return fProvider->ReportMaxReadTransfer ( blockSize, max );
}
IOReturn
IODVDServices::reportMaxValidBlock ( UInt64 * maxBlock )
{
return fProvider->ReportMaxValidBlock ( maxBlock );
}
IOReturn
IODVDServices::reportRemovability ( bool * isRemovable )
{
return fProvider->ReportRemovability ( isRemovable );
}
IOReturn
IODVDServices::readISRC ( UInt8 track, CDISRC isrc )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadISRC ( track, isrc );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readMCN ( CDMCN mcn )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadMCN ( mcn );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readTOC ( IOMemoryDescriptor * buffer )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadTOC ( buffer );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readTOC ( IOMemoryDescriptor * buffer,
CDTOCFormat format,
UInt8 msf,
UInt8 trackSessionNumber,
UInt16 * actualByteCount )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadTOC ( buffer,
format,
msf,
trackSessionNumber,
actualByteCount );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readDiscInfo ( IOMemoryDescriptor * buffer,
UInt16 * actualByteCount )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadDiscInfo ( buffer, actualByteCount );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readTrackInfo ( IOMemoryDescriptor * buffer,
UInt32 address,
CDTrackInfoAddressType addressType,
UInt16 * actualByteCount )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadTrackInfo ( buffer,
address,
addressType,
actualByteCount );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::audioPause ( bool pause )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->AudioPause ( pause );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::audioPlay ( CDMSF timeStart, CDMSF timeStop )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->AudioPlay ( timeStart, timeStop );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::audioScan ( CDMSF timeStart, bool reverse )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->AudioScan ( timeStart, reverse );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::audioStop ( void )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->AudioStop ( );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::getAudioStatus ( CDAudioStatus * cdAudioStatus )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->GetAudioStatus ( cdAudioStatus );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::getAudioVolume ( UInt8 * leftVolume,
UInt8 * rightVolume )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->GetAudioVolume ( leftVolume, rightVolume );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::setAudioVolume ( UInt8 leftVolume, UInt8 rightVolume )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->SetAudioVolume ( leftVolume, rightVolume );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::doSynchronizeCache ( void )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->SynchronizeCache ( );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::reportMaxWriteTransfer ( UInt64 blockSize,
UInt64 * max )
{
return fProvider->ReportMaxWriteTransfer ( blockSize, max );
}
IOReturn
IODVDServices::reportWriteProtection ( bool * isWriteProtected )
{
return fProvider->ReportWriteProtection ( isWriteProtected );
}
UInt32
IODVDServices::getMediaType ( void )
{
return fProvider->GetMediaType ( );
}
IOReturn
IODVDServices::getSpeed ( UInt16 * kilobytesPerSecond )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->GetMediaAccessSpeed ( kilobytesPerSecond );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::setSpeed ( UInt16 kilobytesPerSecond )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->SetMediaAccessSpeed ( kilobytesPerSecond );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::readDVDStructure ( IOMemoryDescriptor * buffer,
const UInt8 structureFormat,
const UInt32 logicalBlockAddress,
const UInt8 layer,
const UInt8 agid )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReadDVDStructure (
buffer,
( UInt32 ) buffer->getLength ( ),
structureFormat,
logicalBlockAddress,
layer,
agid );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::reportKey ( IOMemoryDescriptor * buffer,
const DVDKeyClass keyClass,
const UInt32 lba,
const UInt8 agid,
const DVDKeyFormat keyFormat )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->ReportKey ( buffer,
keyClass,
lba,
agid,
keyFormat );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
IOReturn
IODVDServices::sendKey ( IOMemoryDescriptor * buffer,
const DVDKeyClass keyClass,
const UInt8 agid,
const DVDKeyFormat keyFormat )
{
IOReturn status = kIOReturnNotAttached;
require ( ( isInactive ( ) == false ), ErrorExit );
retain ( );
fProvider->retain ( );
fProvider->CheckPowerState ( );
status = fProvider->SendKey ( buffer,
keyClass,
agid,
keyFormat );
fProvider->release ( );
release ( );
ErrorExit:
return status;
}
bool
IODVDServices::handleOpen ( IOService * client,
IOOptionBits options,
void * access )
{
bool result = false;
if ( ( options & kIOSCSITaskUserClientAccessMask ) == 0 )
{
result = super::handleOpen ( client, options, access );
goto Exit;
}
if ( fClients == NULL )
{
fClients = OSSet::withCapacity ( 1 );
}
require_nonzero ( fClients, ErrorExit );
fClients->setObject ( client );
result = true;
Exit:
ErrorExit:
return result;
}
void
IODVDServices::handleClose ( IOService * client,
IOOptionBits options )
{
if ( ( options & kIOSCSITaskUserClientAccessMask ) == 0 )
{
super::handleClose ( client, options );
}
else
{
fClients->removeObject ( client );
}
}
bool
IODVDServices::handleIsOpen ( const IOService * client ) const
{
bool result = false;
if ( client == NULL )
{
require_nonzero ( fClients, CallSuperClassError );
require_nonzero ( fClients->getCount ( ), CallSuperClassError );
result = true;
}
else
{
require_nonzero ( fClients, CallSuperClassError );
require ( fClients->containsObject ( client ), CallSuperClassError );
result = true;
}
return result;
CallSuperClassError:
result = super::handleIsOpen ( client );
return result;
}
#if 0
#pragma mark -
#pragma mark ₯ Public Static Methods
#pragma mark -
#endif
void
IODVDServices::AsyncReadWriteComplete ( void * clientData,
IOReturn status,
UInt64 actualByteCount )
{
IODVDServices * owner;
IOStorageCompletion returnData;
BlockServicesClientData * bsClientData;
bool commandComplete = true;
bsClientData = ( BlockServicesClientData * ) clientData;
returnData = bsClientData->completionData;
owner = bsClientData->owner;
if ( ( ( status != kIOReturnNotAttached ) &&
( status != kIOReturnOffline ) &&
( status != kIOReturnUnsupportedMode ) &&
( status != kIOReturnNotPrivileged ) &&
( status != kIOReturnSuccess ) ) &&
( bsClientData->retriesLeft > 0 ) )
{
IOReturn requestStatus;
ERROR_LOG ( ( "IODVDServices: AsyncReadWriteComplete retry\n" ) );
bsClientData->retriesLeft--;
if ( bsClientData->clientReadCDCall == true )
{
requestStatus = owner->fProvider->AsyncReadCD (
bsClientData->clientBuffer,
bsClientData->clientStartingBlock,
bsClientData->clientRequestedBlockCount,
bsClientData->clientSectorArea,
bsClientData->clientSectorType,
clientData );
}
else
{
requestStatus = owner->fProvider->AsyncReadWrite (
bsClientData->clientBuffer,
bsClientData->clientStartingBlock,
bsClientData->clientRequestedBlockCount,
clientData );
}
if ( requestStatus != kIOReturnSuccess )
{
commandComplete = true;
}
else
{
commandComplete = false;
}
}
if ( commandComplete == true )
{
IODelete ( clientData, BlockServicesClientData, 1 );
owner->fProvider->release ( );
owner->release ( );
IOStorage::complete ( returnData, status, actualByteCount );
}
}
#if 0
#pragma mark -
#pragma mark ₯ Protected Methods
#pragma mark -
#endif
void
IODVDServices::free ( void )
{
super::free ( );
}
#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif
OSMetaClassDefineReservedUnused ( IODVDServices, 1 );
OSMetaClassDefineReservedUnused ( IODVDServices, 2 );
OSMetaClassDefineReservedUnused ( IODVDServices, 3 );
OSMetaClassDefineReservedUnused ( IODVDServices, 4 );
OSMetaClassDefineReservedUnused ( IODVDServices, 5 );
OSMetaClassDefineReservedUnused ( IODVDServices, 6 );
OSMetaClassDefineReservedUnused ( IODVDServices, 7 );
OSMetaClassDefineReservedUnused ( IODVDServices, 8 );