/* File: IrGlue.cpp Contains: Implementation of clients to IrDA glue */ #include "IrGlue.h" #include "IrLAP.h" #include "IrLMP.h" #include "IrLAPConn.h" #include "IrEvent.h" #include "CTimer.h" #include "IrDiscovery.h" #include "CIrDevice.h" #include "IrQOS.h" #include "IrDALog.h" #include "IrStream.h" #include "IrIASService.h" #include "IrIASServer.h" #include "IrLSAPConn.h" #include "IrDAUserClient.h" #include "AppleIrDA.h" #define private static #if (hasTracing > 0 && hasIrGlueTracing > 0) enum IrGlueTraceCodes { kLogNew = 1, kLogInit, kLogFree, kLogReadComplete, kLogTransmitComplete, kLogSetSpeedComplete, kLogRunQueue, kObtainLSAPIdEvent, kReleaseLSAPIdEvent, kConnectStartEvent, kListenStartEvent, kAllocateEventBlock, kGrabEventBlock, kReleaseEventBlock, kDeleteEventBlock, kStartTerminateEntry, kStartTerminateExit, kLogStartTimer, kLogStopTimer, kLogTimerNotifier, kLogTimerNotifier1, kLogTimerNotifier2, kLogTimerNotifierDone, kLogTimerComplete }; private EventTraceCauseDesc IrGlueTraceEvents[] = { {kLogNew, "irglue: Create, obj="}, {kLogInit, "irglue: Init"}, {kLogFree, "irglue: Free"}, {kLogReadComplete, "irglue: read complete"}, {kLogTransmitComplete, "irglue: transmit complete"}, {kLogSetSpeedComplete, "irglue: set speed complete"}, {kLogRunQueue, "irglue: run event queue"}, {kObtainLSAPIdEvent, "irglue: Obtain LSAP Id"}, {kReleaseLSAPIdEvent, "irglue: Release LSAP Id"}, {kConnectStartEvent, "irglue: Connect request, myid, peerid, deviceaddr"}, {kListenStartEvent, "irglue: Listen request"}, {kAllocateEventBlock, "irglue: Allocate Event Block"}, {kGrabEventBlock, "irglue: Grab Event Block"}, {kReleaseEventBlock, "irglue: Release Event Block"}, {kDeleteEventBlock, "irglue: Delete Event Block"}, {kStartTerminateEntry, "irglue: Disconnected - start"}, {kStartTerminateExit, "irglue: Disconnected - done"}, {kLogStartTimer, "irglue: Start timer delay, #"}, {kLogStopTimer, "irglue: Stop timer #"}, {kLogTimerNotifier, "irglue: timer notifier, id="}, {kLogTimerNotifier1, "irglue: timer notifier, owner="}, {kLogTimerNotifier2, "irglue: timer notifier, timesrc="}, {kLogTimerNotifierDone, "irglue: timer notifier finished"}, {kLogTimerComplete, "irglue: timer complete, signature="} }; #define XTRACE(x, y, z) IrDALogAdd (x, y, z, IrGlueTraceEvents, true ) #else #define XTRACE(x, y, z) ((void)0) #endif extern "C" UInt32 random(void); // from libkern, how to clean up declare? static void TimerNotifier(OSObject *owner, IrDATimerEventSource *sender); // // Todo: move this to a more public spot if they are needed elsewhere // const UInt8 kIASDeviceClassStr[] = "Device"; const UInt8 kIASDeviceNameAttrStr[] = "DeviceName"; const UInt8 kIASDeviceName[] = "Macintosh"; const UInt8 kIASLMPSupportAttrStr[] = "IrLMPSupport"; const UInt8 kIASLMPSupportValue[] = { 0x01, 0x00, 0x00}; // Version 1, no additional IAS support, no additional LMP support //-------------------------------------------------------------------------------- #define super OSObject OSDefineMetaClassAndStructors(TIrGlue, OSObject); //-------------------------------------------------------------------------------- // TIrGlue //-------------------------------------------------------------------------------- /*static*/ TIrGlue * TIrGlue::tIrGlue(AppleIrDASerial *driver, AppleIrDA *appleirda, IOWorkLoop *workloop, USBIrDAQoS *qos) { TIrGlue *obj = new TIrGlue; XTRACE(kLogNew, (int)obj >> 16, (short)obj); if (obj && !obj->init(driver, appleirda, workloop, qos)) { obj->release(); obj = nil; } return obj; } Boolean TIrGlue::init(AppleIrDASerial *driver, AppleIrDA *appleirda, IOWorkLoop *workloop, USBIrDAQoS *qos) { IrDAErr err; int i; XTRACE(kLogInit, 0, 0); fAppleIrDA = appleirda; bzero(fTimers, sizeof(fTimers)); fLSAPIdsInUse = (UInt32) (1 << kNameServerLSAPId); // name server (why doesn't it reserve it?) fIrLAP = nil; fIrLMP = nil; fIrLAPConn = nil; fIrDevice = nil; fIrDiscovery = nil; fMyQOS = fPeerQOS = nil; fNameServer = nil; fNameService = nil; fLastState = kIrDAStatusOff; if (!super::init()) return false; // Set up the the event block list err = TIrEvent::InitEventLists(); nrequire(err, Fail); // Set up the three timers for (i = 0; i < kNumTimers; i++) { fTimers[i] = CTimer::cTimer(workloop, this, &::TimerNotifier); require (fTimers[i], Fail); } // and the two qos objects, lap needs these at init time fMyQOS = TIrQOS::tIrQOS(qos); fPeerQOS = TIrQOS::tIrQOS(qos); // create LAP - todo: can now take the qos pointers out of the event requests fIrLAP = TIrLAP::tIrLAP(this, fMyQOS, fPeerQOS); require(fIrLAP, Fail); // create LMP fIrLMP = TIrLMP::tIrLMP(this); require(fIrLMP, Fail); // create LAPConn fIrLAPConn = TIrLAPConn::tIrLAPConn(this); require(fIrLAPConn, Fail); // create IrDevice fIrDevice = CIrDevice::cIrDevice(this, driver); require(fIrDevice, Fail); fNameService = TIASService::tIASService(); // make our IAS database require(fNameService, Fail); fNameServer = TIASServer::tIASServer(this, fNameService); require(fNameServer, Fail); // Create the discovery object fIrDiscovery = CIrDiscovery::cIrDiscovery(this); require(fIrDiscovery, Fail ); // Finally, load the IAS database with a few defaults require(InitNameService(), Fail); fNameServer->ListenStart(); // start the ias server return true; Fail: // we could free stuff here, but our real free routine will be // called when we're released, wait until then. XTRACE(kLogInit, 0xdead, 0xbeef); return false; } // TIrGlue::Init //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- #define FREE(x) {if (x) { (x)->release(); x = nil; } } void TIrGlue::free() { int i; XTRACE(kLogFree, (int)this >> 16, (short)this); for (i = 0 ; i < kNumTimers; i++) { // first, stop and free the timers FREE(fTimers[i]); } FREE(fMyQOS); FREE(fPeerQOS); FREE(fIrDiscovery); FREE(fIrDevice); FREE(fIrLAPConn); FREE(fIrLMP); FREE(fIrLAP); FREE(fNameServer); FREE(fNameService); TIrEvent::DeleteEventLists(); // release all the pending events (do last) super::free(); } // TIrGlue::free void TIrGlue::ReadComplete(UInt8 *buffer, UInt32 length) { XTRACE(kLogReadComplete, length >> 16, (short)length); if (fIrDevice) fIrDevice->ReadComplete(buffer, length); } void TIrGlue::TransmitComplete(Boolean worked) { XTRACE(kLogTransmitComplete, 0, worked); if (fIrDevice) fIrDevice->TransmitComplete(worked); } void TIrGlue::SetSpeedComplete(Boolean worked) { XTRACE(kLogSetSpeedComplete, 0, worked); if (fIrDevice) fIrDevice->SetSpeedComplete(worked); } void TIrGlue::RunQueue(void) { IrDAStatus status; XTRACE(kLogRunQueue, 0, 0); TIrStream::RunQueue(); XTRACE(kLogRunQueue, 0x1111, 0x1111); GetIrDAStatus(&status); XTRACE(kLogRunQueue, 0x2222, 0x2222); if (fLastState != status.connectionState) { fLastState = status.connectionState; XTRACE(kLogRunQueue, 0x3333, 0x3333); if (fAppleIrDA) fAppleIrDA->messageClients(kIrDACallBack_Status, &fLastState, 1); } XTRACE(kLogRunQueue, 0xffff, 0xffff); } void TIrGlue::Start() { if (fIrDevice) fIrDevice->Start(); fLastState = kIrDAStatusIdle; if (fAppleIrDA) fAppleIrDA->messageClients(kIrDACallBack_Status, &fLastState, 1); } void TIrGlue::Stop() { int i; if (fIrDevice) // first stop trying to talk back to the device fIrDevice->Stop(); // stop all the timers for (i = 0 ; i < kNumTimers; i++) StopTimer(i); Disconnected(true); // reset lap and lmp and the qos objects fLastState = kIrDAStatusOff; if (fAppleIrDA) fAppleIrDA->messageClients(kIrDACallBack_Status, &fLastState, 1); } #pragma mark ---- Event blocks // just layered over IrEvent static functions, used to be imp'd here TIrEvent * TIrGlue::GrabEventBlock(UInt32 event, UInt32 size) { return TIrEvent::GrabEventBlock(event, size); } void TIrGlue::ReleaseEventBlock(TIrEvent* reqBlock) { return TIrEvent::ReleaseEventBlock(reqBlock); } #pragma mark ---- Timers //-------------------------------------------------------------------------------- // StartTimer //-------------------------------------------------------------------------------- void TIrGlue::StartTimer(int id, TTimeout timeDelay, UInt32 refCon) { XTRACE(kLogStartTimer, id, timeDelay); require(id < kNumTimers && id >= 0, Fail); if (fTimers[id]) fTimers[id]->StartTimer(timeDelay, refCon); Fail: return; } // TIrGlue::StartTimer //-------------------------------------------------------------------------------- // StopTimer //-------------------------------------------------------------------------------- void TIrGlue::StopTimer(int id) { XTRACE(kLogStopTimer, 0, id); require(id < kNumTimers && id >= 0, Fail); if (fTimers[id]) fTimers[id]->StopTimer(); Fail: return; } // TIrGlue::StopTimer //-------------------------------------------------------------------------------- // TimerComplete //-------------------------------------------------------------------------------- void TIrGlue::TimerComplete(UInt32 refCon) { XTRACE(kLogTimerComplete, 0, refCon); if ((refCon >= kIrFirstIrLAPTimerEvent) && (refCon <= kIrLastIrLAPTimerEvent)) { fIrLAP->TimerComplete(refCon); // send lap its timers } else if ((refCon >= kIrFirstIrLMPTimerEvent) && (refCon <= kIrLastIrLMPTimerEvent)) { fIrLMP->TimerComplete(refCon); } // Let's run the queue until it's empty now that a timer has fired and // we've probably queued up some work to do. RunQueue(); } // TIrGlue::TimerComplete //-------------------------------------------------------------------------------- // TimerNotifier: Completion proc for all timers //-------------------------------------------------------------------------------- private void TimerNotifier(OSObject *owner, IrDATimerEventSource *iotimer) { TIrGlue *obj; XTRACE(kLogTimerNotifier1, (int)owner >> 16, (short)owner); XTRACE(kLogTimerNotifier2, (int)iotimer >> 16, (short)iotimer); require(owner, Failed); require(iotimer, Failed); obj = OSDynamicCast(TIrGlue, owner); require(obj, Failed); for (int i = 0 ; i < kNumTimers; i++) { CTimer *ctimer = obj->GetTimer(i); if (ctimer && ctimer->GetIOTimer() == iotimer) { // found our CTimer, finally (joke: only 3 of them) XTRACE(kLogTimerNotifier, 0, i); obj->TimerComplete(ctimer->GetSignature()); // call timeout routine XTRACE(kLogTimerNotifierDone, 0xffff, 0xffff); return; } } Failed: XTRACE(kLogTimerNotifier, 0xdead, 0xbeef); return; } #pragma mark ---- Lap Async Disconnect //-------------------------------------------------------------------------------- // Disconnected //-------------------------------------------------------------------------------- void TIrGlue::Disconnected(Boolean reset_lap) { int review_async_disconnect; // check both kinds of disconnect and verify they work XTRACE(kStartTerminateEntry, 0, reset_lap); fMyQOS->Reset(); fPeerQOS->Reset(); if (reset_lap) { fIrLMP->Reset(); // clean up pending i/o fIrLAP->Reset(); // then release the buffers } XTRACE(kStartTerminateExit, 0, 0); } // TIrGlue::Disconnected //-------------------------------------------------------------------------------- // IsLAPConnected (move?) //-------------------------------------------------------------------------------- Boolean TIrGlue::IsLAPConnected(void) // check with lap re our connection status { require(fIrLAP, Fail); return fIrLAP->IsConnected(); Fail: return false; } // TIrGlue::IsLAPConnected // // tell lapconn to disconnect now, don't wait for idle timer to fire // void TIrGlue::DoIdleDisconnect() { require(fIrLAPConn, Fail); fIrLAPConn->DoIdleDisconnect(); Fail: return; } #pragma mark ---- Name and LSAPId Service //-------------------------------------------------------------------------------- // InitNameService //-------------------------------------------------------------------------------- Boolean TIrGlue::InitNameService() { IrDAErr err; // Add an entry for class "Device", attribute "DeviceName" // Set to string "Macintosh" err = fNameService->AddStringEntry(kIASDeviceClassStr, kIASDeviceNameAttrStr, kIASDeviceName, kIASCharSetAscii, 0); nrequire(err, Fail); // Add an entry for class "Device", attribute "IrLMPSupport" // Set to 0x01, 0x00, 0x00 (version 1, no extra ias support, no extra LMP support) err = fNameService->AddNBytesEntry(kIASDeviceClassStr, kIASLMPSupportAttrStr, kIASLMPSupportValue, sizeof(kIASLMPSupportValue)); nrequire(err, Fail); return true; Fail: return false; } // TIrGlue::InitNameService //-------------------------------------------------------------------------------- // ObtainLSAPId //-------------------------------------------------------------------------------- IrDAErr TIrGlue::ObtainLSAPId( UInt32 & desiredLSAPId ) { UByte lsapId; if (fLSAPIdsInUse == 0xFFFFFFFF) { DebugLog("TIrGlue::ObtainLSAPId: no more LSAP Ids available"); return kIrDAErrResourceNotAvailable; } if (desiredLSAPId == kAssignDynamicLSAPId) { lsapId = (UByte)random() & 0x1F; // Random starting point between 0 and 31 } else { if (desiredLSAPId > 0x1F /*kLastValidLSAPId*/) { DebugLog("TIrGlue::ObtainLSAPId: LSAP id out of range"); return kIrDAErrBadParameter; } lsapId = (UByte)desiredLSAPId; // Try to get the one requested } while ((lsapId == 0) || (fLSAPIdsInUse & (1 << lsapId))) { lsapId = (lsapId + 1) & 0x1F; } if ((desiredLSAPId != kAssignDynamicLSAPId) && (lsapId != desiredLSAPId)) { // Wanted a specific lsapId, but its already being taken return kIrDAErrResourceNotAvailable; } desiredLSAPId = (ULong)lsapId; fLSAPIdsInUse |= (1 << lsapId); XTRACE(kObtainLSAPIdEvent, lsapId, fLSAPIdsInUse); return noErr; } // TIrGlue::ObtainLSAPId //-------------------------------------------------------------------------------- // ReleaseLSAPId //-------------------------------------------------------------------------------- void TIrGlue::ReleaseLSAPId( UInt32 lsapId) { check( lsapId != kNameServerLSAPId ); check( lsapId < 32 ); check( fLSAPIdsInUse & (1 << lsapId) ); fLSAPIdsInUse &= ~(1 << lsapId); XTRACE( kReleaseLSAPIdEvent, lsapId, fLSAPIdsInUse ); } // TIrGlue::ReleaseLSAPId //-------------------------------------------------------------------------------- // RegisterMyNameAndLSAPId //-------------------------------------------------------------------------------- IrDAErr TIrGlue::RegisterMyNameAndLSAPId(UInt8 * className, UInt8 * attrName, UInt32 * reqLSAP) { IrDAErr result = kIrDAErrWrongState; UInt32 tempLSAP = *reqLSAP; // Load the clients requested LSAP require(fNameService, Fail); require(className, Fail); require(attrName, Fail); // Obtain the lsapId result = ObtainLSAPId(tempLSAP); nrequire(result, Fail); *reqLSAP = tempLSAP; // Register my lsap-id with the name service result = fNameService->AddIntegerEntry(className, attrName, tempLSAP ); nrequire(result, Fail_RegisterLSAPId); return noErr; Fail_RegisterLSAPId: ReleaseLSAPId( ( UByte )tempLSAP ); Fail: return result; } // TIrGlue::RegisterMyNameAndLSAPId //======================== Start / Completion routines =========================== #pragma mark ---- Connect and Listen //-------------------------------------------------------------------------------- // ConnectStart //-------------------------------------------------------------------------------- IrDAErr TIrGlue::ConnectStart( TIrStream * client, // Caller UInt32 myLSAPId, // Reseved by RegisterMyNameAndLSAPID UInt32 devAddr, // Determined through LAP Discovery UInt32 peerLSAPId, // Determined through IAS Query CBuffer * clientData, TLSAPConn ** theLSAP ) // Returned to caller { TIrConnLstnRequest *connectRequest = nil; TLSAPConn *lsap = nil; XTRACE( kConnectStartEvent, ( myLSAPId<<8 ) + peerLSAPId, devAddr ); connectRequest = (TIrConnLstnRequest*)GrabEventBlock(kIrConnectRequestEvent); require(connectRequest, Fail); if (*theLSAP == nil) { // if the caller doesn't already have an LSAPConn ... lsap = TLSAPConn::tLSAPConn(this, client); // build one require(lsap, Fail_NewLSAPConn); // Assign the lsap-id for the LSAPConn (client already reseved it with RegisterMyNameAndLSAPID) lsap->AssignId(myLSAPId); *theLSAP = lsap; // Give the lsap to the client } else lsap = *theLSAP; // jdg: the caller is giving us the lsapconn from last time // IOLog("glue connnect qos baud is 0x%x\n", GetMyQOS()->SetBaudRate(k57600bps)); // DEBUGGING // Initiate the connect for my LSAPConn connectRequest->fDevAddr = devAddr; connectRequest->fLSAPId = (UInt8)peerLSAPId; connectRequest->fMyQOS = GetMyQOS(); connectRequest->fPeerQOS = GetPeerQOS(); connectRequest->fData = clientData; connectRequest->fClient = client; lsap->EnqueueEvent(connectRequest); return noErr; // Out of memory error exit points Fail_NewLSAPConn: ReleaseEventBlock(connectRequest); Fail: return kIrDAErrNoMemory; } // TIrGlue::ConnectStart //-------------------------------------------------------------------------------- // ListenStart //-------------------------------------------------------------------------------- IrDAErr TIrGlue::ListenStart( TIrStream * client, // Caller UInt32 lsapId, // Preallocated LSAP Id CBuffer * clientData, // Data to pass with connect TLSAPConn ** theLSAP ) // The allocated LSAP { TLSAPConn *lsap; TIrConnLstnRequest *listenRequest; XTRACE(kListenStartEvent, lsapId, 0); listenRequest = (TIrConnLstnRequest *)GrabEventBlock(kIrListenRequestEvent); require(listenRequest, Fail); if (*theLSAP == nil) { // if the caller doesn't already have an LSAPConn ... lsap = TLSAPConn::tLSAPConn(this, client); // Create and init new LSAPConn require(lsap, Fail_NewLSAPConn); // Note: Caller has already registered with IAS lsap->AssignId( lsapId ); // Assign the lsap-id for the LSAPConn *theLSAP = lsap; // return the new lsap to the client } else lsap = *theLSAP; // the caller is giving us the lsapconn from last time listenRequest->fMyQOS = GetMyQOS(); listenRequest->fPeerQOS = GetPeerQOS(); listenRequest->fData = clientData; lsap->EnqueueEvent( listenRequest ); return noErr; Fail_NewLSAPConn: // Out of memory error exit points ReleaseEventBlock(listenRequest); Fail: return kIrDAErrNoMemory; } // TIrGlue::ListenStart // statistics void TIrGlue::GetIrDAStatus(IrDAStatus *status) { Boolean connected = false; Boolean discoverActive = false; Boolean brokenConnection = false; if (fIrDevice && fIrLAP && status) { fIrDevice->GetStatus(status); // get the statistics record (ugh, we stomp on it here) connected = fIrLAP->IsConnected(); brokenConnection = fIrLAP->InBrokenBeam(); discoverActive = fIrLAP->Discovering(); if( brokenConnection ) status->connectionState = kIrDAStatusBrokenConnection; else if( connected ) status->connectionState = kIrDAStatusConnected; else if( discoverActive ) status->connectionState = kIrDAStatusDiscoverActive; else status->connectionState = kIrDAStatusIdle; status->connectionSpeed = fIrDevice->GetSpeed(); fIrLAP->GetNickName(status->nickName); } } #ifdef NOT_NOW #pragma mark ------------------ NOT NOW ---------- extern "C" pascal void XDeferredTaskProc( void *parm ); // test test test test //-------------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------------- NewtonErr TIrGlue::Init() { NewtonErr result; OSErr err; XTRACE(kInit, (int)this >> 16, (short)this); gIrDAPrefs.Init(); // initialize the irda prefs from iqos resources // note this gets Reset each time the ir hardware is changed (ugh!) fMyQOS.Reset(); // now that we have hardware, get matching QoS values fPeerQOS.Reset(); result = InitEventBlockList(); nrequire( result, Fail_Init ); result = fIrLAP.Init(this, &fIrLMP); // Initialize IrLAP -- FIX LAP TO NOT KEEP A LMP PTR nrequire( result, Fail_Init ); result = fIrLMP.Init(this, &fIrLAP); // Initialize IrLMP -- FIX LMP TO NOT KEEP A LAP PTR nrequire( result, Fail_Init ); result = fIrDiscovery.Init( this ); nrequire( result, Fail_Init ); InitNameService(); return noErr; Fail_Init: DeInit(); // reset return -1; } // TIrGlue::Init //-------------------------------------------------------------------------------- // DeInit //-------------------------------------------------------------------------------- void TIrGlue::free(void) { XTRACE(kDeInit, 0, 0); // let's stop the timers right away ... fTimer1.DeInit(); fTimer2.DeInit(); fTimer3.DeInit(); // Free the event block list and any event blocks on it DeleteEventBlockList(); } // TIrGlue::DeInit #endif // NOT_NOW