#include "AppleCuda.h"
#include "AppleCudaUserClient.h"
#include "IOCudaADBController.h"
#include <IOKit/IOLib.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IODeviceMemory.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/assert.h>
#define super IOService
OSDefineMetaClassAndStructors(AppleCuda,IOService)
static void cuda_interrupt ( AppleCuda * self );
static void cuda_process_response(AppleCuda * self);
static void cuda_transmit_data(AppleCuda * self);
static void cuda_expected_attention(AppleCuda * self);
static void cuda_unexpected_attention(AppleCuda * self);
static void cuda_receive_data(AppleCuda * self);
static void cuda_receive_last_byte(AppleCuda * self);
static void cuda_collision(AppleCuda * self);
static void cuda_idle(AppleCuda * self);
static void cuda_poll(AppleCuda * self);
static void cuda_error(AppleCuda * self);
static void cuda_send_request(AppleCuda * self);
static IOReturn cuda_do_sync_request( AppleCuda * self,
cuda_request_t * request, bool polled);
static void cuda_do_state_transition_delay(AppleCuda * self);
static int Cuda_PE_poll_input(unsigned int options, char * c);
static int Cuda_PE_read_write_time_of_day(unsigned int options, long * secs);
static int Cuda_PE_halt_restart(unsigned int type);
static int Cuda_PE_write_IIC(unsigned char addr, unsigned char reg,
unsigned char data);
static void
autopollArrived ( OSObject *inCuda, IOInterruptEventSource *, int );
static int set_cuda_power_message ( int command );
static int set_cuda_file_server_mode ( int command );
static int set_cuda_poweruptime(long secs);
static void cuda_async_set_power_message_enable( thread_call_param_t param, thread_call_param_t );
static void cuda_async_set_file_server_mode( thread_call_param_t param, thread_call_param_t ) ;
bool CudahasRoot( OSObject * us, void *, IOService * yourDevice );
static IOReturn AppleCudaReadIIC( UInt8 address, UInt8 * buffer, IOByteCount * count );
static IOReturn AppleCudaWriteIIC( UInt8 address, const UInt8 * buffer, IOByteCount * count );
static __inline__ unsigned char cuda_read_data(AppleCuda * self)
{
volatile unsigned char val;
val = *self->cuda_via_regs.shift;
eieio();
return( val );
}
static __inline__ int cuda_get_result(cuda_request_t *request)
{
int status = ADB_RET_OK;
int theStatus = request->a_reply.a_header[1];
if ( theStatus & kCudaTimeOutMask )
{
status = ADB_RET_TIMEOUT;
}
else if ( theStatus & kCudaSRQErrorMask )
{
status = ADB_RET_REQUEST_ERROR;
}
else if ( theStatus & kCudaBusErrorMask )
{
status = ADB_RET_BUS_ERROR;
}
return status;
}
static __inline__ void cuda_lock( AppleCuda * self )
{
if( !self->cuda_polled_mode )
IOSimpleLockLock( self->cuda_request_lock );
}
static __inline__ void cuda_unlock( AppleCuda * self )
{
if( !self->cuda_polled_mode )
IOSimpleLockUnlock( self->cuda_request_lock );
}
static AppleCuda * gCuda;
bool AppleCuda::init ( OSDictionary * properties = 0 )
{
return super::init(properties);
}
bool AppleCuda::start ( IOService * nub )
{
int i;
IOMemoryMap * viaMap;
unsigned char * cuda_base;
kprintf( "%s::start being called", getName() );
if( !super::start(nub))
return false;
gCuda = this;
cuda_check_any_interrupt = OSSymbol::withCString("cuda_check_any_interrupt");
cuda_read_i2c = OSSymbol::withCString( "read_iic" );
cuda_write_i2c = OSSymbol::withCString( "write_iic" );
workLoop = NULL;
eventSrc = NULL;
ourADBinterface = NULL;
_rootDomain = 0;
_wakeup_from_sleep = false;
workLoop = IOWorkLoop::workLoop();
if ( !workLoop )
{
kprintf( "%s::start is bailing (workloop creation failed)\n", getName() );
return false;
}
eventSrc = IOInterruptEventSource::interruptEventSource( this, autopollArrived );
if (!eventSrc || ( kIOReturnSuccess != workLoop->addEventSource( eventSrc ) ) )
{
kprintf( "%s::start is bailing (workLoop->addEvent failed)\n", getName() );
return false;
}
if( 0 == (viaMap = nub->mapDeviceMemoryWithIndex( 0 )) )
{
IOLog( "%s: no via memory\n", getName() );
kprintf( "%s::start is bailing (memory mapping failed)\n", getName() );
return( false );
}
cuda_base = (unsigned char *)viaMap->getVirtualAddress();
kprintf( "%s: VIA base = %08x\n", getName(), (UInt32)cuda_base );
ourADBinterface = new IOCudaADBController;
if ( !ourADBinterface )
{
kprintf( "%s::start is bailing (no ADB interface available)\n", getName() );
return( false );
}
if ( !ourADBinterface->init(0,this) )
{
kprintf( "%s::start is bailing (ADB init failed)\n", getName() );
return( false );
}
if ( !ourADBinterface->attach( this) )
{
kprintf( "%s::start is bailing (ADB attach failed)\n", getName() );
return( false );
}
cuda_request_lock = IOSimpleLockAlloc();
IOSimpleLockInit( cuda_request_lock );
cuda_via_regs.dataB = cuda_base;
cuda_via_regs.handshakeDataA = cuda_base+0x0200;
cuda_via_regs.dataDirectionB = cuda_base+0x0400;
cuda_via_regs.dataDirectionA = cuda_base+0x0600;
cuda_via_regs.timer1CounterLow = cuda_base+0x0800;
cuda_via_regs.timer1CounterHigh = cuda_base+0x0A00;
cuda_via_regs.timer1LatchLow = cuda_base+0x0C00;
cuda_via_regs.timer1LatchHigh = cuda_base+0x0E00;
cuda_via_regs.timer2CounterLow = cuda_base+0x1000;
cuda_via_regs.timer2CounterHigh = cuda_base+0x1200;
cuda_via_regs.shift = cuda_base+0x1400;
cuda_via_regs.auxillaryControl = cuda_base+0x1600;
cuda_via_regs.peripheralControl = cuda_base+0x1800;
cuda_via_regs.interruptFlag = cuda_base+0x1A00;
cuda_via_regs.interruptEnable = cuda_base+0x1C00;
cuda_via_regs.dataA = cuda_base+0x1E00;
clock_interval_to_absolutetime_interval(200, 1, &cuda_state_transition_delay);
*cuda_via_regs.dataDirectionB |= (kCudaByteAcknowledgeMask | kCudaTransferInProgressMask);
*cuda_via_regs.dataDirectionB &= ~kCudaTransferRequestMask;
*cuda_via_regs.auxillaryControl = (*cuda_via_regs.auxillaryControl | kCudaTransferMode) &
kCudaSystemRecieve;
if ( *cuda_via_regs.shift )
;
cuda_interrupt_state = CUDA_STATE_IDLE;
cuda_transaction_state = CUDA_TS_NO_REQUEST;
cuda_is_header_transfer = false;
cuda_is_packet_type = false;
cuda_transfer_count = 0;
cuda_current_response = NULL;
for( i = 0; i < NUM_AP_BUFFERS; i++ )
{
cuda_unsolicited[ i ].a_buffer = cuda_autopoll_buffers[ i ];
}
cuda_neg_tip_and_byteack( this );
IOSleep( 4 );
(void)cuda_read_data( this );
cuda_assert_byte_ack( this );
cuda_wait_for_transfer_request_assert( this );
cuda_wait_for_interrupt( this );
(void)cuda_read_data( this );
cuda_neg_byte_ack( this );
cuda_wait_for_transfer_request_neg( this );
cuda_wait_for_interrupt( this );
cuda_neg_transfer_in_progress( this );
(void)cuda_read_data( this );
#define VIA_DEV_CUDA 2
nub->registerInterrupt(VIA_DEV_CUDA,
this, (IOInterruptAction) cuda_interrupt);
nub->enableInterrupt(VIA_DEV_CUDA);
PE_poll_input = Cuda_PE_poll_input;
PE_read_write_time_of_day = Cuda_PE_read_write_time_of_day;
PE_halt_restart = Cuda_PE_halt_restart;
PE_write_IIC = Cuda_PE_write_IIC;
publishResource( "IOiic0", this );
publishResource( "IORTC", this );
_power_message_thread = thread_call_allocate ((thread_call_func_t) cuda_async_set_power_message_enable, (thread_call_param_t)this);
thread_call_enter (_power_message_thread);
_fileserver_thread = thread_call_allocate((thread_call_func_t)
cuda_async_set_file_server_mode,
(thread_call_param_t)this);
thread_call_enter( _fileserver_thread );
registerService();
getPMRootDomain()->publishFeature( "FileServer" );
_cuda_power_state = 1; addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"),
(IOServiceNotificationHandler)CudahasRoot, this, 0 );
ourADBinterface->start( this );
kprintf( "%s::start exiting normally\n", getName() );
return true;
}
bool CudahasRoot( OSObject * us, void *, IOService * yourDevice )
{
if (( yourDevice != NULL ) && ( ((AppleCuda *)us)->_rootDomain == 0) )
{
((AppleCuda *)us)->_rootDomain = (IOPMrootDomain *) yourDevice;
((IOPMrootDomain *)yourDevice)->registerInterestedDriver((IOService *) us);
}
return true;
}
IOReturn AppleCuda::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
IOService* unused2)
{
if ( ! (theFlags & IOPMPowerOn) )
{
_cuda_power_state = 0; }
return IOPMAckImplied;
}
IOReturn AppleCuda::powerStateDidChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
IOService* unused2)
{
if (theFlags & IOPMPowerOn)
{
_cuda_power_state = 1; _wakeup_from_sleep = false; }
return IOPMAckImplied;
}
IOWorkLoop *AppleCuda::getWorkLoop() const
{
return( workLoop );
}
void AppleCuda::free ( void )
{
if ( workLoop )
{
workLoop->release();
}
if ( eventSrc )
{
eventSrc->release();
}
if ( ourADBinterface )
{
ourADBinterface->release();
}
if (_rootDomain)
{
_rootDomain->deRegisterInterestedDriver((IOService *) this);
_rootDomain = 0;
}
super::free();
}
void AppleCuda::registerForADBInterrupts ( ADB_callback_func handler, IOService * caller )
{
autopoll_handler = handler;
ADBid = caller;
}
static void autopollArrived ( OSObject * CudaDriver, IOInterruptEventSource *, int )
{
((AppleCuda *)CudaDriver)->serviceAutopolls();
}
#define RB_BOOT 1
extern "C" {
void boot(int paniced, int howto, char * command);
}
static void cuda_async_set_power_message_enable( thread_call_param_t param, thread_call_param_t )
{
set_cuda_power_message( kADB_powermsg_enable );
}
static void cuda_async_set_file_server_mode( thread_call_param_t param, thread_call_param_t )
{
AppleCuda * me = (AppleCuda *) param;
me->setFileServerMode( false );
}
void AppleCuda::serviceAutopolls ( void )
{
cuda_packet_t * response;
while( inIndex != outIndex )
{
response = &cuda_unsolicited[ outIndex ];
if (response->a_header[0] == ADB_PACKET_POWER)
{
unsigned char flag, cmd;
flag = response->a_header[1];
cmd = response->a_header[2];
if ( (flag == kADB_powermsg_flag_chassis) &&
(cmd == kADB_powermsg_cmd_chassis_off) )
{
AbsoluteTime deadline;
if (_rootDomain)
{
if (_cuda_power_state)
{
_rootDomain->receivePowerNotification (kIOPMSleepNow);
}
else {
_rootDomain->receivePowerNotification(kIOPMPowerButton);
}
}
clock_interval_to_deadline(1, kSecondScale, &deadline);
thread_call_enter_delayed(_power_message_thread, deadline);
}
else if ((flag == kADB_powermsg_flag_keyboardpwr)
&& (cmd == kADB_powermsg_cmd_keyboardoff))
{
thread_call_enter (_power_message_thread);
}
}
if ( ADBid != NULL )
{
(*autopoll_handler)(ADBid,response->a_header[2],response->a_bcount,response->a_buffer);
}
outIndex = (outIndex + 1) & (NUM_AP_BUFFERS - 1);
}
}
IOReturn AppleCuda::doSyncRequest ( cuda_request_t * request )
{
return( cuda_do_sync_request(this, request, false) );
}
static IOReturn
AppleCudaWriteIIC( UInt8 address, const UInt8 * buffer, IOByteCount * count )
{
IOReturn ret;
cuda_request_t cmd;
if( !gCuda )
return( kIOReturnUnsupported );
adb_init_request( &cmd );
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_GET_SET_IIC;
cmd.a_cmd.a_header[2] = address;
cmd.a_cmd.a_hcount = 3;
cmd.a_cmd.a_buffer = (UInt8 *) buffer;
cmd.a_cmd.a_bcount = *count;
ret = cuda_do_sync_request( gCuda, &cmd, true );
*count = cmd.a_cmd.a_bcount;
return( ret );
}
static IOReturn
AppleCudaReadIIC( UInt8 address, UInt8 * buffer, IOByteCount * count )
{
IOReturn ret;
cuda_request_t cmd;
if( !gCuda )
return( kIOReturnUnsupported );
adb_init_request( &cmd );
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_GET_SET_IIC;
cmd.a_cmd.a_header[2] = address;
cmd.a_cmd.a_hcount = 3;
cmd.a_reply.a_buffer = buffer;
cmd.a_reply.a_bcount = *count;
ret = cuda_do_sync_request( gCuda, &cmd, true );
*count = cmd.a_reply.a_bcount;
return( ret );
}
IOReturn AppleCuda::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
IOReturn ioReturn = kIOReturnBadArgument;
if (functionName == cuda_check_any_interrupt)
{
bool *hasint;
hasint = (bool *)param1;
*hasint = false;
if ( inIndex != outIndex )
{
*hasint = true;
}
if ( _wakeup_from_sleep )
{
*hasint = true;
}
ioReturn = kIOReturnSuccess;
}
else if ( functionName == cuda_read_i2c )
{
if ( ( ((UInt32) param1) & ~0xFF ) == 0 )
ioReturn = AppleCudaReadIIC( (UInt8) param1,
(UInt8 *) param2,
(IOByteCount *) param3 );
}
else if ( functionName == cuda_write_i2c )
{
if ( ( ((UInt32) param1) & ~0xFF ) == 0 )
ioReturn = AppleCudaWriteIIC( (UInt8) param1,
(UInt8 *) param2,
(IOByteCount *) param3 );
}
else
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
return( ioReturn );
}
void
AppleCuda::setWakeTime( UInt32 waketime )
{
_wakeup_from_sleep = false;
if (waketime != 0)
{
timerSrc = IOTimerEventSource::timerEventSource( (OSObject*)this, WakeupTimeoutHandler );
if ( !timerSrc || (workLoop->addEventSource(timerSrc) != kIOReturnSuccess) )
{
IOLog( "Cuda can not register timeout event\n" );
return;
}
timerSrc->setTimeoutMS( waketime );
}
}
void
AppleCuda::WakeupTimeoutHandler( OSObject *object, IOTimerEventSource *timer )
{
gCuda->_wakeup_from_sleep = true;
if ( gCuda->_rootDomain )
{
gCuda->_rootDomain->activityTickle( 0,0 );
}
}
void
AppleCuda::setPowerOnTime( UInt32 newTime )
{
long long_secs;
if ( newTime != 0 )
{
Cuda_PE_read_write_time_of_day( kPEReadTOD, &long_secs );
set_cuda_poweruptime( (long)newTime + long_secs );
}
}
void
AppleCuda::setFileServerMode( bool fileServerMode )
{
set_cuda_file_server_mode( (int) fileServerMode );
setProperty( "FileServer", (bool) fileServerMode );
}
#if 0
void
AppleCuda::setWakeOnRing( bool setForWake )
{
}
#endif
void AppleCuda::demandSleepNow(void)
{
if (_rootDomain)
{
_rootDomain->receivePowerNotification( kIOPMSleepNow );
}
}
#define kAppleCudaUserClientMagicCookie 0x0C00DA
#define kApplePMUUserClientMagicCookie 0x0101BEEF // soon to be "generic" PM user client magic cookie
IOReturn
AppleCuda::newUserClient(task_t owningTask,
void * securityToken,
UInt32 magicCookie,
IOUserClient ** handler)
{
IOReturn ioReturn = kIOReturnSuccess;
AppleCudaUserClient * client = NULL;
if ( IOUserClient::clientHasPrivilege(securityToken, "root") != kIOReturnSuccess )
{
IOLog( "%s::newUserClient: Can't create user client, not privileged\n", getName() );
return( kIOReturnNotPrivileged );
}
switch( magicCookie )
{
case kAppleCudaUserClientMagicCookie:
case kApplePMUUserClientMagicCookie:
client = AppleCudaUserClient::withTask( owningTask );
if ( client == NULL )
{
ioReturn = kIOReturnNoResources;
IOLog( "%s::newUserClient: Unable to create user client\n", getName() );
}
break;
default:
ioReturn = kIOReturnInvalid;
IOLog( "%s::newUserClient: bad magic cookie\n", getName() );
}
if ( ioReturn == kIOReturnSuccess )
{
if ( client->attach(this) == false )
{
ioReturn = kIOReturnError;
IOLog( "%s::newUserClient: Unable to attach user client\n", getName() );
}
}
if ( ioReturn == kIOReturnSuccess )
{
if ( client->start(this) == false )
{
ioReturn = kIOReturnError;
IOLog( "%s::newUserClientt: Unable to start user client\n", getName() );
}
}
if ( (ioReturn != kIOReturnSuccess) && (client != NULL) )
{
client->detach(this);
client->release();
}
*handler = client;
return( ioReturn );
}
IOReturn cuda_do_sync_request ( AppleCuda * self, cuda_request_t * request, bool polled )
{
bool wasPolled = false;
IOInterruptState ints;
if( !polled )
{
request->sync = IOSyncer::create();
request->needWake = true;
}
ints = IOSimpleLockLockDisableInterrupt( self->cuda_request_lock );
if ( polled )
{
wasPolled = self->cuda_polled_mode;
self->cuda_polled_mode = polled;
}
if ( self->cuda_last_request )
self->cuda_last_request->a_next = request;
else
self->cuda_request = request;
self->cuda_last_request = request;
if ( self->cuda_interrupt_state == CUDA_STATE_IDLE )
cuda_send_request(self);
if ( polled )
{
cuda_poll( self );
self->cuda_polled_mode = wasPolled;
assert( 0 == self->cuda_request );
assert( 0 == self->cuda_last_request );
}
IOSimpleLockUnlockEnableInterrupt( self->cuda_request_lock, ints );
if( !polled )
request->sync->wait();
return cuda_get_result(request);
}
static int Cuda_PE_read_write_time_of_day ( unsigned int options, long * secs )
{
cuda_request_t cmd;
adb_init_request( &cmd );
cmd.a_cmd.a_hcount = 2;
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
switch( options )
{
case kPEReadTOD:
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_GET_REAL_TIME;
cmd.a_reply.a_buffer = (UInt8 *)secs;
cmd.a_reply.a_bcount = sizeof(*secs);
break;
case kPEWriteTOD:
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_SET_REAL_TIME;
cmd.a_cmd.a_buffer = (UInt8 *)secs;
cmd.a_cmd.a_bcount = sizeof(*secs);
break;
default:
return 1;
}
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int Cuda_PE_halt_restart ( unsigned int type )
{
cuda_request_t cmd;
adb_init_request( &cmd );
cmd.a_cmd.a_hcount = 2;
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
switch( type )
{
case kPERestartCPU:
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_RESTART_SYSTEM;
break;
case kPEHaltCPU:
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_POWER_DOWN;
break;
default:
return( 1 );
}
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int set_cuda_poweruptime( long secs )
{
cuda_request_t cmd;
long localsecs = secs;
adb_init_request( &cmd );
cmd.a_cmd.a_hcount = 2;
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_SET_POWER_UPTIME;
cmd.a_cmd.a_buffer = (UInt8 *)&localsecs;
cmd.a_cmd.a_bcount = 4;
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int set_cuda_file_server_mode ( int command )
{
cuda_request_t cmd;
adb_init_request( &cmd );
cmd.a_cmd.a_hcount = 3;
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_FILE_SERVER_FLAG;
cmd.a_cmd.a_header[2] = command;
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int set_cuda_power_message ( int command )
{
cuda_request_t cmd;
if ( command >= kADB_powermsg_invalid )
return 0;
adb_init_request( &cmd );
cmd.a_cmd.a_hcount = 3;
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_SET_POWER_MESSAGES;
cmd.a_cmd.a_header[2] = command;
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int Cuda_PE_write_IIC ( unsigned char addr, unsigned char reg, unsigned char data )
{
cuda_request_t cmd;
adb_init_request(&cmd);
cmd.a_cmd.a_header[0] = ADB_PACKET_PSEUDO;
cmd.a_cmd.a_header[1] = ADB_PSEUDOCMD_GET_SET_IIC;
cmd.a_cmd.a_header[2] = addr;
cmd.a_cmd.a_header[3] = reg;
cmd.a_cmd.a_header[4] = data;
cmd.a_cmd.a_hcount = 5;
return( cuda_do_sync_request( gCuda, &cmd, true ) );
}
static int Cuda_PE_poll_input ( unsigned int, char * c )
{
AppleCuda * self = gCuda;
int interruptflag;
UInt8 code;
cuda_packet_t * response;
static char keycodes2ascii[] = "asdfhgzxcv_bqwer" "yt123465=97-80]o" "u[ip\nlj'k;_,/nm." "\t_";
*c = 0xff;
if( !self )
{
return 1;
}
self->cuda_polled_mode = true;
interruptflag = *self->cuda_via_regs.interruptFlag & kCudaInterruptMask;
eieio();
if( interruptflag )
{
cuda_interrupt( self );
}
if( self->inIndex != self->outIndex )
{
response = &self->cuda_unsolicited[ self->outIndex ];
if( ((response->a_header[2] >> 4) == 2)
&& (response->a_bcount > 1) )
{
code = response->a_buffer[0];
if( code < sizeof(keycodes2ascii) )
{
*c = keycodes2ascii[ code ];
}
}
self->outIndex = self->inIndex;
}
self->cuda_polled_mode = false;
return( 0 );
}
static void cuda_send_request ( AppleCuda * self )
{
if( !cuda_is_transfer_in_progress( self ) )
{
cuda_set_data_direction_to_output( self );
cuda_write_data( self, self->cuda_request->a_cmd.a_header[0] );
self->cuda_is_header_transfer = true;
self->cuda_transfer_count = 1;
cuda_neg_byte_ack( self );
cuda_assert_transfer_in_progress( self );
self->cuda_interrupt_state = CUDA_STATE_TRANSMIT_EXPECTED;
self->cuda_transaction_state = CUDA_TS_SYNC_RESPONSE;
}
#if 0
else
{
IOLog("Req = %x, state = %x, TIP = %x\n", self->cuda_request,
self->cuda_interrupt_state, cuda_is_transfer_in_progress(self));
}
#endif
}
static void cuda_poll( AppleCuda * self )
{
do
{
cuda_wait_for_interrupt( self );
cuda_interrupt( self );
} while( self->cuda_interrupt_state != CUDA_STATE_IDLE );
}
static void cuda_process_response ( AppleCuda * self )
{
volatile cuda_request_t * request;
unsigned int newIndex;
if ( self->cuda_transaction_state == CUDA_TS_SYNC_RESPONSE )
{
cuda_lock( self );
request = self->cuda_request;
if( NULL == ( self->cuda_request = request->a_next ) )
{
self->cuda_last_request = NULL;
}
cuda_unlock( self );
if ( ((cuda_request_t *)request)->needWake )
{
((cuda_request_t *)request)->sync->signal();
}
}
else
{
if ( self->cuda_transaction_state == CUDA_TS_ASYNC_RESPONSE )
{
newIndex = (self->inIndex + 1) & (NUM_AP_BUFFERS - 1);
if( newIndex != self->outIndex )
{
self->inIndex = newIndex;
}
#if 0
else
{
}
#endif
if ( !self->cuda_polled_mode )
{
self->eventSrc->interruptOccurred(0, 0, 0);
}
}
}
return;
}
static void cuda_interrupt ( AppleCuda * self )
{
unsigned char interruptState;
interruptState = cuda_get_interrupt_state( self );
switch ( interruptState )
{
case kCudaReceiveByte:
cuda_receive_data( self );
break;
case kCudaReceiveLastByte:
cuda_receive_last_byte( self );
break;
case kCudaTransmitByte:
cuda_transmit_data( self );
break;
case kCudaUnexpectedAttention:
cuda_unexpected_attention( self );
break;
case kCudaExpectedAttention:
cuda_expected_attention( self );
break;
case kCudaIdleState:
cuda_idle( self );
break;
case kCudaCollision:
cuda_collision( self );
break;
default:
cuda_error( self );
break;
}
}
static void cuda_transmit_data ( AppleCuda * self )
{
if ( self->cuda_is_header_transfer )
{
cuda_write_data( self, self->cuda_request->a_cmd.a_header[self->cuda_transfer_count++] );
if ( self->cuda_transfer_count >= self->cuda_request->a_cmd.a_hcount )
{
self->cuda_is_header_transfer = FALSE;
self->cuda_transfer_count = 0;
}
cuda_toggle_byte_ack( self );
}
else
{
if ( self->cuda_transfer_count < self->cuda_request->a_cmd.a_bcount )
{
cuda_write_data( self,
*(self->cuda_request->a_cmd.a_buffer + self->cuda_transfer_count++) );
cuda_toggle_byte_ack( self );
}
else
{
(void)cuda_read_data( self );
cuda_neg_tip_and_byteack( self );
self->cuda_interrupt_state = CUDA_STATE_ATTN_EXPECTED;
}
}
}
static void cuda_expected_attention ( AppleCuda * self )
{
(void)cuda_read_data( self );
cuda_do_state_transition_delay( self );
if ( self->cuda_transaction_state == CUDA_TS_SYNC_RESPONSE )
{
self->cuda_current_response = (cuda_packet_t*)&self->cuda_request->a_reply;
}
else
{
self->cuda_current_response = &self->cuda_unsolicited[ self->inIndex ];
self->cuda_current_response->a_hcount = 0;
self->cuda_current_response->a_bcount = MAX_AP_RESPONSE;
}
self->cuda_is_header_transfer = true;
self->cuda_is_packet_type = true;
self->cuda_transfer_count = 0;
cuda_set_data_direction_to_input( self );
cuda_assert_transfer_in_progress( self );
self->cuda_interrupt_state = CUDA_STATE_RECEIVE_EXPECTED;
}
static void cuda_unexpected_attention ( AppleCuda * self )
{
(void)cuda_read_data( self );
self->cuda_current_response = &self->cuda_unsolicited[ self->inIndex ];
self->cuda_current_response->a_hcount = 0;
self->cuda_current_response->a_bcount = MAX_AP_RESPONSE;
self->cuda_is_header_transfer = TRUE;
self->cuda_is_packet_type = TRUE;
self->cuda_transfer_count = 0;
cuda_assert_transfer_in_progress( self );
self->cuda_interrupt_state = CUDA_STATE_RECEIVE_EXPECTED;
self->cuda_transaction_state = CUDA_TS_ASYNC_RESPONSE;
}
static void cuda_receive_data ( AppleCuda * self )
{
if ( self->cuda_is_packet_type )
{
unsigned char packetType;
packetType = cuda_read_data( self );
self->cuda_current_response->a_header[self->cuda_transfer_count++] = packetType;
if ( packetType == ADB_PACKET_ERROR )
{
self->cuda_current_response->a_hcount = 4;
}
else
{
self->cuda_current_response->a_hcount = 3;
}
self->cuda_is_packet_type = false;
cuda_toggle_byte_ack( self );
}
else
{
if ( self->cuda_is_header_transfer )
{
self->cuda_current_response->a_header[self->cuda_transfer_count++] =
cuda_read_data( self );
if ( self->cuda_transfer_count >= self->cuda_current_response->a_hcount )
{
self->cuda_is_header_transfer = FALSE;
self->cuda_transfer_count = 0;
}
cuda_toggle_byte_ack( self );
}
else
{
if ( self->cuda_transfer_count < self->cuda_current_response->a_bcount )
{
*(self->cuda_current_response->a_buffer + self->cuda_transfer_count++) =
cuda_read_data( self );
cuda_toggle_byte_ack( self );
}
else
{
self->cuda_current_response->a_bcount = self->cuda_transfer_count;
cuda_read_data( self );
cuda_process_response( self );
cuda_neg_tip_and_byteack( self );
}
}
}
}
static void cuda_receive_last_byte ( AppleCuda * self )
{
if ( self->cuda_is_header_transfer )
{
self->cuda_current_response->a_header[self->cuda_transfer_count++] =
cuda_read_data( self );
self->cuda_transfer_count = 0;
}
else
{
if ( self->cuda_transfer_count < self->cuda_current_response->a_bcount )
{
*(self->cuda_current_response->a_buffer + self->cuda_transfer_count++) =
cuda_read_data( self );
}
else
{
(void) cuda_read_data( self );
}
}
self->cuda_current_response->a_bcount = self->cuda_transfer_count;
cuda_neg_tip_and_byteack( self );
cuda_process_response( self );
}
static void cuda_collision ( AppleCuda * self )
{
(void)cuda_read_data( self );
cuda_neg_transfer_in_progress( self );
self->cuda_interrupt_state = CUDA_STATE_ATTN_EXPECTED;
self->cuda_transaction_state = CUDA_TS_ASYNC_RESPONSE;
self->cuda_is_header_transfer = false;
self->cuda_transfer_count = 0;
}
static void cuda_idle ( AppleCuda * self )
{
(void)cuda_read_data( self );
cuda_lock( self );
self->cuda_interrupt_state = CUDA_STATE_IDLE;
if( self->cuda_request )
{
cuda_send_request( self );
}
cuda_unlock( self );
}
static void cuda_error ( AppleCuda * self )
{
switch ( self->cuda_interrupt_state )
{
case CUDA_STATE_IDLE:
cuda_neg_tip_and_byteack( self );
break;
case CUDA_STATE_TRANSMIT_EXPECTED:
if ( self->cuda_is_header_transfer && self->cuda_transfer_count <= 1 )
{
cuda_do_state_transition_delay( self );
cuda_neg_transfer_in_progress( self );
cuda_set_data_direction_to_input( self );
panic (" CUDA - TODO FORCE COMMAND BACK UP!\n" );
}
else
{
self->cuda_interrupt_state = CUDA_STATE_ATTN_EXPECTED;
cuda_neg_tip_and_byteack( self );
}
break;
case CUDA_STATE_ATTN_EXPECTED:
cuda_assert_transfer_in_progress( self );
cuda_do_state_transition_delay( self );
cuda_set_data_direction_to_input( self );
cuda_neg_transfer_in_progress( self );
panic("CUDA - TODO CHECK FOR TRANSACTION TYPE AND ERROR");
break;
case CUDA_STATE_RECEIVE_EXPECTED:
cuda_neg_tip_and_byteack( self );
panic( "Cuda - TODO check for transaction type and error\n" );
break;
default:
cuda_set_data_direction_to_input( self );
cuda_neg_tip_and_byteack( self );
break;
}
}
static void cuda_do_state_transition_delay( AppleCuda * self )
{
AbsoluteTime deadline;
clock_absolutetime_interval_to_deadline( self->cuda_state_transition_delay,
&deadline);
clock_delay_until( deadline );
}