#include <IOKit/IODeviceTreeSupport.h> // (gIODTPlane, ...)
#include <IOKit/IOLib.h> // (IONew, ...)
#include <IOKit/storage/IOMedia.h>
#define super IOStorage
OSDefineMetaClassAndStructors(IOMedia, IOStorage)
#ifndef __LP64__
extern IOStorageAttributes gIOStorageAttributesUnsupported;
#endif
enum
{
kIOStorageAccessWriter = 0x00000002,
kIOStorageAccessInvalid = 0x0000000D,
kIOStorageAccessReservedMask = 0xFFFFFFF0
};
static UInt8 gIOMediaAccessTable[8][8] =
{
{ 000, 001, 002, 003, 006, 006, 006, 000 },
{ 011, 006, 011, 006, 006, 006, 006, 011 },
{ 002, 001, 002, 003, 006, 006, 006, 002 },
{ 003, 006, 003, 003, 006, 006, 006, 003 },
{ 006, 006, 006, 006, 006, 006, 006, 004 },
{ 006, 006, 006, 006, 006, 006, 006, 015 },
{ 006, 006, 006, 006, 006, 006, 006, 006 },
{ 006, 006, 006, 006, 006, 006, 006, 006 }
};
class IOMediaAccess
{
protected:
IOStorageAccess _access;
public:
inline void operator=( IOStorageAccess access )
{
_access = access;
}
inline void operator=( OSObject * access )
{
if ( access )
{
operator=( ( ( OSNumber * ) access )->unsigned32BitValue( ) );
}
else
{
operator=( kIOStorageAccessNone );
}
}
inline void operator+=( IOStorageAccess access )
{
_access = ( ( _access - 1 ) >> 1 ) & 7;
_access = gIOMediaAccessTable[ ( ( access - 1 ) >> 1 ) & 7 ][ _access ];
_access = ( ( _access & 7 ) << 1 ) + 1;
}
inline void operator+=( OSObject * access )
{
if ( access )
{
operator+=( ( ( OSNumber * ) access )->unsigned32BitValue( ) );
}
}
inline operator IOStorageAccess( )
{
return _access;
}
};
IOStorage * IOMedia::getProvider() const
{
return (IOStorage *) IOService::getProvider();
}
bool IOMedia::matchPropertyTable(OSDictionary * table, SInt32 * score)
{
if (super::matchPropertyTable(table, score) == false) return false;
return compareProperty(table, kIOMediaContentKey ) &&
compareProperty(table, kIOMediaContentHintKey ) &&
compareProperty(table, kIOMediaEjectableKey ) &&
compareProperty(table, kIOMediaLeafKey ) &&
compareProperty(table, kIOMediaOpenKey ) &&
compareProperty(table, kIOMediaPreferredBlockSizeKey) &&
compareProperty(table, kIOMediaRemovableKey ) &&
compareProperty(table, kIOMediaSizeKey ) &&
compareProperty(table, kIOMediaUUIDKey ) &&
compareProperty(table, kIOMediaWholeKey ) &&
compareProperty(table, kIOMediaWritableKey );
}
#ifndef __LP64__
bool IOMedia::init(UInt64 base,
UInt64 size,
UInt64 preferredBlockSize,
bool isEjectable,
bool isWhole,
bool isWritable,
const char * contentHint,
OSDictionary * properties)
{
IOMediaAttributeMask attributes = 0;
attributes |= isEjectable ? kIOMediaAttributeEjectableMask : 0;
attributes |= isEjectable ? kIOMediaAttributeRemovableMask : 0;
return init( base,
size,
preferredBlockSize,
attributes,
isWhole,
isWritable,
contentHint,
properties );
}
#endif
void IOMedia::free(void)
{
if (_openClients) _openClients->release();
super::free();
}
bool IOMedia::attachToChild(IORegistryEntry * client,
const IORegistryPlane * plane)
{
OSString * s;
if (super::attachToChild(client, plane) == false) return false;
s = OSDynamicCast(OSString, client->getProperty(gIOMatchCategoryKey));
if (s && s->isEqualTo(kIOStorageCategory))
{
setProperty(kIOMediaLeafKey, false);
s = OSDynamicCast(OSString,client->getProperty(kIOMediaContentMaskKey));
if (s) setProperty(kIOMediaContentKey, s->getCStringNoCopy());
}
return true;
}
void IOMedia::detachFromChild(IORegistryEntry * client,
const IORegistryPlane * plane)
{
OSString * s;
s = OSDynamicCast(OSString, client->getProperty(gIOMatchCategoryKey));
if (s && s->isEqualTo(kIOStorageCategory))
{
setProperty(kIOMediaContentKey, getContentHint());
setProperty(kIOMediaLeafKey, true);
}
super::detachFromChild(client, plane);
}
bool IOMedia::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
IOMediaAccess access;
IOStorageAccess accessIn;
IOService * driver;
IOMediaAccess level;
OSObject * object;
OSIterator * objects;
bool rebuild;
bool success;
bool teardown;
assert( client );
access = _openClients->getObject( ( OSSymbol * ) client );
accessIn = ( uintptr_t ) argument;
driver = 0;
rebuild = false;
success = false;
teardown = false;
object = ( OSObject * ) OSSymbol::withCString( kIOStorageCategory );
if ( object == 0 )
{
goto handleOpenErr;
}
driver = copyClientWithCategory( ( OSSymbol * ) object );
object->release( );
objects = OSCollectionIterator::withCollection( _openClients );
if ( objects == 0 )
{
goto handleOpenErr;
}
level = kIOStorageAccessNone;
while ( ( object = objects->getNextObject( ) ) )
{
if ( object != client )
{
level += _openClients->getObject( ( OSSymbol * ) object );
}
}
objects->release( );
level += accessIn;
if ( level == kIOStorageAccessInvalid )
{
goto handleOpenErr;
}
if ( ( accessIn & kIOStorageAccessWriter ) )
{
if ( _isWritable == false )
{
goto handleOpenErr;
}
if ( ( accessIn & kIOStorageAccessSharedLock ) == false )
{
if ( driver )
{
if ( driver != client )
{
teardown = true;
}
}
}
}
else
{
if ( ( access & kIOStorageAccessWriter ) )
{
rebuild = true;
}
}
if ( isInactive( ) )
{
if ( access == kIOStorageAccessNone )
{
goto handleOpenErr;
}
if ( ( accessIn & kIOStorageAccessWriter ) )
{
goto handleOpenErr;
}
}
if ( teardown )
{
if ( _openClients->getObject( ( OSSymbol * ) driver ) )
{
goto handleOpenErr;
}
if ( driver->terminate( kIOServiceSynchronous ) == false )
{
goto handleOpenErr;
}
}
level = ( level & kIOStorageAccessReaderWriter );
if ( _openLevel != level )
{
IOStorage * provider;
provider = OSDynamicCast( IOStorage, getProvider( ) );
if ( provider )
{
success = provider->open( this, options, level );
if ( success == false )
{
if ( teardown )
{
registerService( kIOServiceAsynchronous );
}
goto handleOpenErr;
}
setProperty( kIOMediaOpenKey, true );
}
}
success = true;
object = OSNumber::withNumber( accessIn, 32 );
assert( object );
_openClients->setObject( ( OSSymbol * ) client, object );
_openLevel = level;
object->release( );
if ( rebuild )
{
if ( isInactive( ) == false )
{
if ( driver )
{
if ( driver != client )
{
driver->requestProbe( 0 );
}
}
else
{
registerService( kIOServiceAsynchronous );
}
}
}
handleOpenErr:
if ( driver )
{
driver->release( );
}
return success;
}
bool IOMedia::handleIsOpen(const IOService * client) const
{
if ( client )
{
return _openClients->getObject( ( OSSymbol * ) client ) ? true : false;
}
else
{
return _openClients->getCount( ) ? true : false;
}
}
void IOMedia::handleClose(IOService * client, IOOptionBits options)
{
IOMediaAccess access;
IOService * driver;
IOMediaAccess level;
OSObject * object;
OSIterator * objects;
assert( client );
assert( _openClients->getObject( ( OSSymbol * ) client ) );
access = _openClients->getObject( ( OSSymbol * ) client );
driver = 0;
object = ( OSObject * ) OSSymbol::withCString( kIOStorageCategory );
if ( object == 0 )
{
goto handleCloseErr;
}
driver = copyClientWithCategory( ( OSSymbol * ) object );
object->release( );
objects = OSCollectionIterator::withCollection( _openClients );
if ( objects == 0 )
{
goto handleCloseErr;
}
level = kIOStorageAccessNone;
while ( ( object = objects->getNextObject( ) ) )
{
if ( object != client )
{
level += _openClients->getObject( ( OSSymbol * ) object );
}
}
objects->release( );
level = ( level & kIOStorageAccessReaderWriter );
if ( _openLevel != level )
{
IOStorage * provider;
provider = OSDynamicCast( IOStorage, getProvider( ) );
if ( provider )
{
if ( level == kIOStorageAccessNone )
{
provider->close( this, options );
setProperty( kIOMediaOpenKey, false );
}
else
{
bool success;
success = provider->open( this, 0, level );
assert( success );
}
}
}
_openClients->removeObject( ( OSSymbol * ) client );
_openLevel = level;
if ( ( access & kIOStorageAccessWriter ) )
{
if ( isInactive( ) == false )
{
if ( driver )
{
if ( driver != client )
{
driver->requestProbe( 0 );
}
}
else
{
registerService( kIOServiceAsynchronous );
}
}
}
handleCloseErr:
if ( driver )
{
driver->release( );
}
}
void IOMedia::read(IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageAttributes * attributes,
IOStorageCompletion * completion)
{
#ifndef __LP64__
if (IOStorage::_expansionData)
{
if (attributes == &gIOStorageAttributesUnsupported)
{
attributes = NULL;
}
else
{
IOStorage::read(client, byteStart, buffer, attributes, completion);
return;
}
}
#endif
if (isInactive())
{
complete(completion, kIOReturnNoMedia);
return;
}
if (_openLevel == kIOStorageAccessNone) {
complete(completion, kIOReturnNotOpen);
return;
}
if (_mediaSize == 0 || _preferredBlockSize == 0)
{
complete(completion, kIOReturnUnformattedMedia);
return;
}
if (buffer == 0)
{
complete(completion, kIOReturnBadArgument);
return;
}
if (_mediaSize < byteStart + buffer->getLength())
{
complete(completion, kIOReturnBadArgument);
return;
}
byteStart += _mediaBase;
getProvider()->read(this, byteStart, buffer, attributes, completion);
}
void IOMedia::write(IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageAttributes * attributes,
IOStorageCompletion * completion)
{
#ifndef __LP64__
if (IOStorage::_expansionData)
{
if (attributes == &gIOStorageAttributesUnsupported)
{
attributes = NULL;
}
else
{
IOStorage::write(client, byteStart, buffer, attributes, completion);
return;
}
}
#endif
if (isInactive())
{
complete(completion, kIOReturnNoMedia);
return;
}
if (_openLevel == kIOStorageAccessNone) {
complete(completion, kIOReturnNotOpen);
return;
}
if (_openLevel == kIOStorageAccessReader) {
#ifdef __LP64__
complete(completion, kIOReturnNotPrivileged);
return;
#endif
}
if (_isWritable == 0)
{
complete(completion, kIOReturnLockedWrite);
return;
}
if (_mediaSize == 0 || _preferredBlockSize == 0)
{
complete(completion, kIOReturnUnformattedMedia);
return;
}
if (buffer == 0)
{
complete(completion, kIOReturnBadArgument);
return;
}
if (_mediaSize < byteStart + buffer->getLength())
{
complete(completion, kIOReturnBadArgument);
return;
}
byteStart += _mediaBase;
getProvider()->write(this, byteStart, buffer, attributes, completion);
}
IOReturn IOMedia::synchronizeCache(IOService * client)
{
if (isInactive())
{
return kIOReturnNoMedia;
}
if (_openLevel == kIOStorageAccessNone) {
return kIOReturnNotOpen;
}
if (_openLevel == kIOStorageAccessReader) {
#ifdef __LP64__
return kIOReturnNotPrivileged;
#endif
}
if (_isWritable == 0)
{
return kIOReturnLockedWrite;
}
if (_mediaSize == 0 || _preferredBlockSize == 0)
{
return kIOReturnUnformattedMedia;
}
return getProvider()->synchronizeCache(this);
}
IOReturn IOMedia::discard(IOService * client,
UInt64 byteStart,
UInt64 byteCount)
{
if (isInactive())
{
return kIOReturnNoMedia;
}
if (_openLevel == kIOStorageAccessNone) {
return kIOReturnNotOpen;
}
if (_openLevel == kIOStorageAccessReader) {
#ifdef __LP64__
return kIOReturnNotPrivileged;
#endif
}
if (_isWritable == 0)
{
return kIOReturnLockedWrite;
}
if (_mediaSize == 0 || _preferredBlockSize == 0)
{
return kIOReturnUnformattedMedia;
}
if (_mediaSize < byteStart + byteCount)
{
return kIOReturnBadArgument;
}
byteStart += _mediaBase;
return getProvider()->discard(this, byteStart, byteCount);
}
UInt64 IOMedia::getPreferredBlockSize() const
{
return _preferredBlockSize;
}
UInt64 IOMedia::getSize() const
{
return _mediaSize;
}
UInt64 IOMedia::getBase() const
{
return _mediaBase;
}
bool IOMedia::isEjectable() const
{
return (_attributes & kIOMediaAttributeEjectableMask) ? true : false;
}
bool IOMedia::isFormatted() const
{
return (_mediaSize && _preferredBlockSize);
}
bool IOMedia::isWritable() const
{
return _isWritable;
}
bool IOMedia::isWhole() const
{
return _isWhole;
}
const char * IOMedia::getContent() const
{
OSString * string;
string = OSDynamicCast(OSString, getProperty(kIOMediaContentKey));
if (string == 0) return "";
return string->getCStringNoCopy();
}
const char * IOMedia::getContentHint() const
{
OSString * string;
string = OSDynamicCast(OSString, getProperty(kIOMediaContentHintKey));
if (string == 0) return "";
return string->getCStringNoCopy();
}
bool IOMedia::init(UInt64 base,
UInt64 size,
UInt64 preferredBlockSize,
IOMediaAttributeMask attributes,
bool isWhole,
bool isWritable,
const char * contentHint,
OSDictionary * properties)
{
bool isEjectable;
bool isRemovable;
if (_openClients == 0)
{
if (super::init(properties) == false) return false;
}
isEjectable = (attributes & kIOMediaAttributeEjectableMask) ? true : false;
isRemovable = (attributes & kIOMediaAttributeRemovableMask) ? true : false;
if (isEjectable)
{
attributes |= kIOMediaAttributeRemovableMask;
isRemovable = true;
}
_attributes = attributes;
_mediaBase = base;
_isWhole = isWhole;
_isWritable = isWritable;
_preferredBlockSize = preferredBlockSize;
#ifdef __LP64__
_mediaSize = size;
#else
if (size > _mediaSize)
{
*((volatile UInt64 *) &_mediaSize) = (size & (UINT64_MAX ^ UINT32_MAX)) | (_mediaSize & UINT32_MAX);
}
else
{
*((volatile UInt64 *) &_mediaSize) = (size & UINT32_MAX) | (_mediaSize & (UINT64_MAX ^ UINT32_MAX));
}
*((volatile UInt64 *) &_mediaSize) = size;
#endif
if (_openClients == 0)
{
_openClients = OSDictionary::withCapacity(2);
_openLevel = kIOStorageAccessNone;
if (_openClients == 0) return false;
setProperty(kIOMediaContentKey, contentHint ? contentHint : "");
setProperty(kIOMediaLeafKey, true);
setProperty(kIOMediaOpenKey, false);
}
else
{
IOService * driver;
OSObject * object;
object = (OSObject *) OSSymbol::withCString(kIOStorageCategory);
if (object == 0) return false;
driver = copyClientWithCategory((OSSymbol *) object);
object->release();
object = 0;
if (driver)
{
object = OSDynamicCast(OSString, driver->getProperty(kIOMediaContentMaskKey));
driver->release();
}
if (object == 0) setProperty(kIOMediaContentKey, contentHint ? contentHint : "");
}
setProperty(kIOMediaContentHintKey, contentHint ? contentHint : "");
setProperty(kIOMediaEjectableKey, isEjectable);
setProperty(kIOMediaPreferredBlockSizeKey, preferredBlockSize, 64);
setProperty(kIOMediaRemovableKey, isRemovable);
setProperty(kIOMediaSizeKey, size, 64);
setProperty(kIOMediaWholeKey, isWhole);
setProperty(kIOMediaWritableKey, isWritable);
return true;
}
IOMediaAttributeMask IOMedia::getAttributes() const
{
return _attributes;
}
#ifdef __LP64__
OSMetaClassDefineReservedUnused(IOMedia, 0);
OSMetaClassDefineReservedUnused(IOMedia, 1);
#else
OSMetaClassDefineReservedUsed(IOMedia, 0);
OSMetaClassDefineReservedUsed(IOMedia, 1);
#endif
OSMetaClassDefineReservedUnused(IOMedia, 2);
OSMetaClassDefineReservedUnused(IOMedia, 3);
OSMetaClassDefineReservedUnused(IOMedia, 4);
OSMetaClassDefineReservedUnused(IOMedia, 5);
OSMetaClassDefineReservedUnused(IOMedia, 6);
OSMetaClassDefineReservedUnused(IOMedia, 7);
OSMetaClassDefineReservedUnused(IOMedia, 8);
OSMetaClassDefineReservedUnused(IOMedia, 9);
OSMetaClassDefineReservedUnused(IOMedia, 10);
OSMetaClassDefineReservedUnused(IOMedia, 11);
OSMetaClassDefineReservedUnused(IOMedia, 12);
OSMetaClassDefineReservedUnused(IOMedia, 13);
OSMetaClassDefineReservedUnused(IOMedia, 14);
OSMetaClassDefineReservedUnused(IOMedia, 15);
#ifndef __LP64__
extern "C" void _ZN7IOMedia4readEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOMedia * media, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
{
media->read( client, byteStart, buffer, NULL, &completion );
}
extern "C" void _ZN7IOMedia5writeEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOMedia * media, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
{
media->write( client, byteStart, buffer, NULL, &completion );
}
#endif