IOADBController.cpp [plain text]
#include <mach/mach_types.h>
#include "IOADBControllerUserClient.h"
#include <IOKit/adb/IOADBController.h>
#include <IOKit/adb/IOADBDevice.h>
#include <libkern/c++/OSSymbol.h>
#include <libkern/c++/OSNumber.h>
#include <IOKit/IOLib.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include "IOADBBusPriv.h"
bool ADBhasRoot( OSObject *, void *, IOService * );
void doProbe ( thread_call_param_t, thread_call_param_t);
#define kTenSeconds 10000000
#define super IOADBBus
OSDefineMetaClass(IOADBController,IOADBBus)
OSDefineAbstractStructors(IOADBController,IOADBBus)
bool IOADBController::start ( IOService * nub )
{
if( !super::start(nub)) {
return false;
}
probeBus();
rootDomain = NULL;
busProbed = true;
probeThread = thread_call_allocate((thread_call_func_t)doProbe, (thread_call_param_t)this);
if (probeThread == NULL) {
IOLog("IOADBController::start fails to call thread_call_allocate \n");
return false;
}
addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"), (IOServiceNotificationHandler)ADBhasRoot, this, 0 );
return true;
}
bool ADBhasRoot( OSObject * us, void *, IOService * yourDevice )
{
if ( yourDevice != NULL ) {
((IOADBController *)us)->rootDomain = (IOPMrootDomain *)yourDevice;
((IOADBController *)us)->rootDomain->registerInterestedDriver((IOService *) us);
}
return true;
}
IOReturn IOADBController::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long, IOService*)
{
int i;
if ( ! (theFlags & IOPMPowerOn) ) {
busProbed = false;
for ( i = 1; i < ADB_DEVICE_COUNT; i++ ) {
if( adbDevices[ i ] != NULL ) {
if ( adbDevices[ i ]->nub ) {
adbDevices[ i ]->nub->terminate(kIOServiceRequired);
adbDevices[ i ]->nub->release();
}
IOFree( adbDevices[ i ], sizeof (ADBDeviceControl));
adbDevices[ i ] = NULL;
}
}
}
return IOPMAckImplied;
}
IOReturn IOADBController::powerStateDidChangeTo ( IOPMPowerFlags theFlags, unsigned long, IOService*)
{
if ( theFlags & IOPMPowerOn ) {
if ( ! busProbed ) {
thread_call_enter(probeThread);
busProbed = true;
return kTenSeconds;
}
}
return IOPMAckImplied;
}
void doProbe ( thread_call_param_t arg, thread_call_param_t)
{
((IOADBController *)arg)->probeBus();
((IOADBController *)arg)->rootDomain->acknowledgePowerChange((IOService *)arg);
}
bool IOADBController::probeAddress ( IOADBAddress addr )
{
IOReturn err;
ADBDeviceControl * deviceInfo;
UInt16 value;
IOByteCount length;
length = 2;
err = readFromDevice(addr,3,(UInt8 *)&value,&length);
if (err == ADB_RET_OK) {
if( NULL == (deviceInfo = adbDevices[ addr ])) {
deviceInfo = (ADBDeviceControl *)IOMalloc(sizeof(ADBDeviceControl));
bzero(deviceInfo, sizeof(ADBDeviceControl));
adbDevices[ addr ] = deviceInfo;
deviceInfo->defaultAddress = addr;
deviceInfo->handlerID = deviceInfo->defaultHandlerID = (value & 0xff);
}
deviceInfo->address = addr;
}
return( (err == ADB_RET_OK));
}
unsigned int IOADBController::firstBit ( unsigned int mask )
{
int bit = 15;
while( 0 == (mask & (1 << bit))) {
bit--;
}
return(bit);
}
bool IOADBController::moveDeviceFrom ( IOADBAddress from, IOADBAddress to, bool check )
{
IOReturn err;
UInt16 value;
IOByteCount length;
bool moved;
length = 2;
value = ((to << 8) | ADB_DEVCMD_CHANGE_ID);
err = writeToDevice(from,3,(UInt8 *)&value,&length);
adbDevices[ to ] = adbDevices[ from ];
moved = probeAddress(to);
if( moved || (!check)) {
adbDevices[ from ] = NULL;
}
else {
adbDevices[ to ] = NULL;
}
return moved;
}
IOReturn IOADBController::probeBus ( void )
{
int i;
UInt32 unresolvedAddrs;
UInt32 freeAddrs;
IOADBAddress freeNum, devNum;
IOADBDevice * newDev;
OSDictionary * newProps;
char nameStr[ 10 ];
const OSNumber * object;
const OSSymbol * key;
IOSleep(1500);
setAutoPollEnable(false);
resetBus();
IOSleep(1500);
unresolvedAddrs = 0;
freeAddrs = 0xfffe;
for (i = 1; i < ADB_DEVICE_COUNT; i++) {
if( probeAddress(i) ) {
unresolvedAddrs |= ( 1 << i );
freeAddrs &= ~( 1 << i );
}
}
while( unresolvedAddrs) {
if( !freeAddrs) {
panic("ADB: Cannot find a free ADB slot for reassignment!");
}
freeNum = firstBit(freeAddrs);
devNum = firstBit(unresolvedAddrs);
if( !moveDeviceFrom(devNum, freeNum, true) ) {
IOLog("WARNING : ADB DEVICE %d having problems "
"probing!\n", devNum);
}
else {
if( probeAddress(devNum) ) {
freeAddrs &= ~( 1 << freeNum );
devNum = 0;
}
else {
moveDeviceFrom(freeNum,devNum,false);
}
}
if(devNum) {
unresolvedAddrs &= ~( 1 << devNum );
}
}
IOLog("ADB present:%lx\n", (freeAddrs ^ 0xfffe));
setAutoPollList(freeAddrs ^ 0xfffe);
setAutoPollPeriod(11111);
setAutoPollEnable(true);
for ( i = 1; i < ADB_DEVICE_COUNT; i++ ) {
if( 0 == adbDevices[ i ] ) {
continue;
}
newDev = new IOADBDevice; if ( newDev == NULL ) {
continue;
}
adbDevices[ i ]->nub = newDev;
newProps = OSDictionary::withCapacity( 10 ); if ( newProps == NULL ) {
newDev->free();
continue;
}
key = OSSymbol::withCString(ADBaddressProperty); if ( key == NULL ) {
newDev->free();
newProps->free();
continue;
}
object = OSNumber::withNumber((unsigned long long)adbDevices[i]->address,8);
if ( object == NULL ) {
key->release();
newDev->free();
newProps->free();
continue;
}
newProps->setObject(key, (OSObject *)object); key->release();
object->release();
key = OSSymbol::withCString(ADBhandlerIDProperty); if ( key == NULL ) {
newDev->free();
newProps->free();
continue;
}
object = OSNumber::withNumber((unsigned long long)adbDevices[i]->handlerID,8);
if ( object == NULL ) {
key->release();
newDev->free();
newProps->free();
continue;
}
newProps->setObject(key, (OSObject *)object); key->release();
object->release();
key = OSSymbol::withCString(ADBdefAddressProperty); if ( key == NULL ) {
newDev->free();
newProps->free();
continue;
}
object = OSNumber::withNumber((unsigned long long)adbDevices[i]->defaultAddress,8);
if ( object == NULL ) {
key->release();
newDev->free();
newProps->free();
continue;
}
newProps->setObject(key, (OSObject *)object); key->release();
object->release();
key = OSSymbol::withCString(ADBdefHandlerProperty); if ( key == NULL ) {
newDev->free();
newProps->free();
continue;
}
object = OSNumber::withNumber((unsigned long long)adbDevices[i]->defaultHandlerID,8);
if ( object == NULL ) {
key->release();
newDev->free();
newProps->free();
continue;
}
newProps->setObject(key, (OSObject *)object); key->release();
object->release();
if ( ! newDev->init(newProps,adbDevices[i]) ) { kprintf("adb nub init failed\n");
newDev->release();
continue;
}
sprintf(nameStr,"%x-%02x",adbDevices[i]->defaultAddress,adbDevices[i]->handlerID);
newDev->setName(nameStr);
sprintf(nameStr, "%x", adbDevices[i]->defaultAddress);
newDev->setLocation(nameStr);
newProps->release(); if ( !newDev->attach(this) ) {
kprintf("adb nub attach failed\n");
newDev->release();
continue;
}
newDev->registerService();
newDev->start(this);
} return kIOReturnSuccess;
}
void autopollHandler ( IOService * us, UInt8 adbCommand, IOByteCount length, UInt8 * data )
{
((IOADBController *)us)->packet(data,length,adbCommand);
}
void IOADBController::packet ( UInt8 * data, IOByteCount length, UInt8 adbCommand )
{
ADBDeviceControl * deviceInfo;
deviceInfo = adbDevices[ adbCommand >> 4 ];
if( deviceInfo != NULL ) {
if( deviceInfo->owner != NULL ) {
deviceInfo->handler(deviceInfo->owner, adbCommand, length, data);
}
}
else {
}
}
bool IOADBController::matchNubWithPropertyTable( IOService * device, OSDictionary * propTable )
{
bool matched = false;
const char * keys;
ADBDeviceControl * deviceInfo = (ADBDeviceControl *)(((IOADBDevice *)device)->busRef());
OSObject * X;
do {
X = propTable->getObject("ADB Match");
if( !X ) {
break;
}
keys = ((OSString *)X)->getCStringNoCopy();
if( *keys == '*' ) {
keys++;
}
else {
if( deviceInfo->defaultAddress != strtol(keys, &keys, 16)) {
break;
}
}
if( *keys++ == '-' ) {
if( deviceInfo->defaultHandlerID != strtol(keys, &keys, 16)) {
break;
}
}
matched = true;
} while ( false );
return matched;
}
IOReturn IOADBController::setOwner ( void * device, IOService * client, ADB_callback_func handler )
{
ADBDeviceControl * deviceInfo = (ADBDeviceControl *)device;
deviceInfo->owner = client;
deviceInfo->handler = handler;
return kIOReturnSuccess;
}
IOReturn IOADBController::clearOwner ( void * device )
{
ADBDeviceControl * deviceInfo = (ADBDeviceControl *)device;
kprintf("IOADBController::clearOwner\n");
deviceInfo->owner = NULL;
deviceInfo->handler = NULL;
return kIOReturnSuccess;
}
IOReturn IOADBController::claimDevice (unsigned long ADBaddress, IOService * client, ADB_callback_func handler )
{
if ( claimed_devices[ADBaddress] == true ) { return kIOReturnExclusiveAccess; }
if ( adbDevices[ADBaddress] == NULL ) { return kIOReturnNoDevice; }
if (adbDevices[ADBaddress]->handler != NULL ) { return kIOReturnExclusiveAccess; }
claimed_devices[ADBaddress] = true; return kIOReturnSuccess;
}
IOReturn IOADBController::releaseDevice (unsigned long ADBaddress )
{
if ( claimed_devices[ADBaddress] == false ) {
return kIOReturnBadArgument;
}
claimed_devices[ADBaddress] = false;
return kIOReturnSuccess;
}
IOReturn IOADBController::readDeviceForUser (unsigned long address, unsigned long adbRegister,
UInt8 * data, IOByteCount * length)
{
if ( claimed_devices[address] == false ) {
return kIOReturnBadArgument;
}
return (readFromDevice((IOADBAddress)address,(IOADBRegister)adbRegister,data,length));
}
IOReturn IOADBController::writeDeviceForUser (unsigned long address, unsigned long adbRegister,
UInt8 * data, IOByteCount * length)
{
if ( claimed_devices[address] == false ) {
return kIOReturnBadArgument;
}
return (writeToDevice((IOADBAddress)address,(IOADBRegister)adbRegister,data,length));
}
IOADBAddress IOADBController::address ( ADBDeviceControl * busRef )
{
return busRef->address;
}
IOADBAddress IOADBController::defaultAddress ( ADBDeviceControl * busRef )
{
return busRef->defaultAddress;
}
UInt8 IOADBController::handlerID ( ADBDeviceControl * busRef )
{
return busRef->handlerID;
}
UInt8 IOADBController::defaultHandlerID ( ADBDeviceControl * busRef )
{
return busRef->defaultHandlerID;
}
IOReturn IOADBController::flush ( ADBDeviceControl * busRef )
{
return(flushDevice(busRef->address));
}
IOReturn IOADBController::readRegister ( ADBDeviceControl * busRef, IOADBRegister adbRegister,
UInt8 * data, IOByteCount * length )
{
return readFromDevice(busRef->address,adbRegister,data,length);
}
IOReturn IOADBController::writeRegister ( ADBDeviceControl * busRef, IOADBRegister adbRegister,
UInt8 * data, IOByteCount * length )
{
return writeToDevice(busRef->address,adbRegister,data,length);
}
IOReturn IOADBController::setHandlerID ( ADBDeviceControl * deviceInfo, UInt8 handlerID )
{
IOReturn err;
UInt16 value;
IOByteCount length;
IOADBAddress addr = deviceInfo->address;
length = 2;
err = readFromDevice(addr,3,(UInt8 *)&value,&length);
if ( err ) {
return err;
}
value = (value & 0xf000) | handlerID | (addr << 8);
length = sizeof(value);
err = writeToDevice(addr,3,(UInt8 *)&value,&length);
length = sizeof(value);
err = readFromDevice(addr,3,(UInt8 *)&value,&length);
if ( err == kIOReturnSuccess ) {
deviceInfo->handlerID = value & 0xff;
}
if ( deviceInfo->handlerID == handlerID ) {
err = kIOReturnSuccess;
}
else {
err = kIOReturnNoResources;
}
return err;
}
int IOADBController::getURLComponentUnit ( IOService * device, char * path, int maxLen )
{
ADBDeviceControl * deviceInfo = (ADBDeviceControl *)((IOADBDevice *)device)->busRef();
if( maxLen > 1 ) {
sprintf( path, "%x", deviceInfo->address );
return(1);
}
else {
return(0);
}
}
IOReturn IOADBController::newUserClient( task_t owningTask, void * , UInt32 type, IOUserClient ** handler )
{
IOReturn err = kIOReturnSuccess;
IOADBControllerUserClient * client;
client = IOADBControllerUserClient::withTask(owningTask);
if( !client || (false == client->attach( this )) ||
(false == client->start( this )) ) {
if(client) {
client->detach( this );
client->release();
client = NULL;
}
err = kIOReturnNoMemory;
}
*handler = client;
return err;
}