#include <IOKit/IOLib.h>
#include <IOKit/IOLocks.h>
#include <IOKit/assert.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/graphics/IOAccelerator.h>
#include <IOKit/graphics/IOGraphicsTypesPrivate.h>
#include <IOKit/IOUserClient.h>
OSDefineMetaClassAndStructors(IOAccelerator, IOService)
static IOLock * gLock;
static queue_head_t gGlobalList;
static UInt32 gTotalCount;
static SInt32 gTweak;
struct IOAccelIDRecord
{
IOAccelID id;
SInt32 retain;
queue_chain_t task_link;
queue_chain_t glob_link;
};
enum { kTweakBits = 0x1f };
class IOAccelerationUserClient : public IOUserClient
{
OSDeclareDefaultStructors( IOAccelerationUserClient );
private:
task_t fTask;
queue_head_t fTaskList;
static void initialize();
public:
virtual bool start( IOService * provider );
virtual void stop( IOService * provider );
virtual bool initWithTask( task_t owningTask, void * securityID,
UInt32 type, OSDictionary * properties );
virtual IOReturn clientClose( void );
virtual IOExternalMethod * getTargetAndMethodForIndex(
IOService ** targetP, UInt32 index );
IOReturn extCreate(IOOptionBits options,
IOAccelID requestedID, IOAccelID * idOut);
IOReturn extDestroy(IOOptionBits options, IOAccelID id);
};
#define super IOUserClient
OSDefineMetaClassAndStructorsWithInit(IOAccelerationUserClient, IOUserClient,
IOAccelerationUserClient::initialize());
void IOAccelerationUserClient::initialize()
{
if (!gLock)
{
gLock = IOLockAlloc();
queue_init(&gGlobalList);
}
}
bool IOAccelerationUserClient::initWithTask( task_t owningTask, void * securityID,
UInt32 type, OSDictionary * properties )
{
if ( properties != NULL )
properties->setObject ( kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue );
fTask = owningTask;
queue_init(&fTaskList);
return( super::initWithTask( owningTask, securityID, type, properties ));
}
bool IOAccelerationUserClient::start( IOService * provider )
{
if( !super::start( provider ))
return( false );
return (true);
}
IOReturn IOAccelerationUserClient::clientClose( void )
{
if( !isInactive())
terminate();
return( kIOReturnSuccess );
}
void IOAccelerationUserClient::stop( IOService * provider )
{
IOAccelIDRecord * record;
IOLockLock(gLock);
while (!queue_empty( &fTaskList ))
{
queue_remove_first( &fTaskList,
record,
IOAccelIDRecord *,
task_link );
if (--record->retain)
record->task_link.next = 0;
else
{
queue_remove(&gGlobalList,
record,
IOAccelIDRecord *,
glob_link);
gTotalCount--;
IODelete(record, IOAccelIDRecord, 1);
}
}
IOLockUnlock(gLock);
super::stop( provider );
}
IOExternalMethod * IOAccelerationUserClient::getTargetAndMethodForIndex(
IOService ** targetP, UInt32 index )
{
static const IOExternalMethod methodTemplate[] =
{
{ NULL, (IOMethod) &IOAccelerationUserClient::extCreate,
kIOUCScalarIScalarO, 2, 1 },
{ NULL, (IOMethod) &IOAccelerationUserClient::extDestroy,
kIOUCScalarIScalarO, 2, 0 },
};
if (index >= (sizeof(methodTemplate) / sizeof(methodTemplate[0])))
return (NULL);
*targetP = this;
return ((IOExternalMethod *)(methodTemplate + index));
}
static
IOReturn _CreateID(queue_head_t * taskList, IOOptionBits options,
IOAccelID requestedID, IOAccelID * idOut)
{
IOReturn err;
Boolean found;
IOAccelIDRecord * record;
IOAccelIDRecord * dup;
record = IONew(IOAccelIDRecord, 1);
record->retain = 1;
IOLockLock(gLock);
gTotalCount++;
do
{
if (kIOAccelSpecificID & options)
{
if ((requestedID > 4095) || (requestedID < -4096))
{
err = kIOReturnExclusiveAccess;
break;
}
found = false;
queue_iterate(&gGlobalList,
dup,
IOAccelIDRecord *,
glob_link)
{
found = (dup->id == requestedID);
if (found)
break;
}
if (found)
{
err = kIOReturnExclusiveAccess;
break;
}
record->id = requestedID;
}
else
{
record->id = ((IOAccelID) (intptr_t) record) ^ (kTweakBits & gTweak++);
}
if (taskList)
{
queue_enter(taskList, record,
IOAccelIDRecord *, task_link);
}
else
record->task_link.next = 0;
queue_enter(&gGlobalList, record,
IOAccelIDRecord *, glob_link);
*idOut = record->id;
err = kIOReturnSuccess;
}
while (false);
if (kIOReturnSuccess != err)
gTotalCount--;
IOLockUnlock(gLock);
if (kIOReturnSuccess != err)
{
IODelete(record, IOAccelIDRecord, 1);
}
return (err);
}
IOReturn IOAccelerationUserClient::extCreate(IOOptionBits options,
IOAccelID requestedID, IOAccelID * idOut)
{
return (_CreateID(&fTaskList, options, requestedID, idOut));
}
IOReturn IOAccelerationUserClient::extDestroy(IOOptionBits options, IOAccelID id)
{
IOAccelIDRecord * record;
bool found = false;
IOLockLock(gLock);
queue_iterate(&fTaskList,
record,
IOAccelIDRecord *,
task_link)
{
found = (record->id == id);
if (found)
{
queue_remove(&fTaskList,
record,
IOAccelIDRecord *,
task_link);
if (--record->retain)
record->task_link.next = 0;
else
{
queue_remove(&gGlobalList,
record,
IOAccelIDRecord *,
glob_link);
gTotalCount--;
IODelete(record, IOAccelIDRecord, 1);
}
break;
}
}
IOLockUnlock(gLock);
return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
}
IOReturn
IOAccelerator::createAccelID(IOOptionBits options, IOAccelID * identifier)
{
return (_CreateID(0, options, *identifier, identifier));
}
IOReturn
IOAccelerator::retainAccelID(IOOptionBits options, IOAccelID id)
{
IOAccelIDRecord * record;
bool found = false;
IOLockLock(gLock);
queue_iterate(&gGlobalList,
record,
IOAccelIDRecord *,
glob_link)
{
found = (record->id == id);
if (found)
{
record->retain++;
break;
}
}
IOLockUnlock(gLock);
return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
}
IOReturn
IOAccelerator::releaseAccelID(IOOptionBits options, IOAccelID id)
{
IOAccelIDRecord * record;
bool found = false;
IOLockLock(gLock);
queue_iterate(&gGlobalList,
record,
IOAccelIDRecord *,
glob_link)
{
found = (record->id == id);
if (found)
{
if (!--record->retain)
{
if (record->task_link.next)
panic("IOAccelerator::releaseID task_link");
queue_remove(&gGlobalList,
record,
IOAccelIDRecord *,
glob_link);
gTotalCount--;
IODelete(record, IOAccelIDRecord, 1);
}
break;
}
}
IOLockUnlock(gLock);
return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
}