/* File: IrLAP.cpp Contains: Implementation of IrLAP */ #define IRDA_TEST_FRAME_SUPPORTED 1 // required I think #include "IrLAP.h" #include "IrLMP.h" #include "CList.h" #include "IrGlue.h" #include "CListIterator.h" #include "IrDscInfo.h" #include "IrQOS.h" #include "IrDiscovery.h" #include "IrLSAPConn.h" #include "CIrDevice.h" //#include // for random() #define forMac 1 // TEMP TEMP TEMP -- clean up the code extern "C" ULong random(); // from libkern, will it link?? const UByte IrSlotCounts[4] = {1, 6, 8, 16}; // valid discovery slots #if (hasTracing > 0 && hasLAPTracing > 0) enum IrLAPTraceCodes { kLogCreate = 1, kLogFree, kLogInit, kLogInit2, kUnexpectedEvent, kLogStateEvent, kNDMTimeoutEvent, kNDMRecdInputEvent, kNDMDiscoveryEvent, kNDMConnectEvent, kNDMListenEvent, kNDMDisconnectEvent, kQueryTimeoutEvent, kQueryRecdInputEvent, kQueryOutputDoneEvent, kQueryDisconnectEvent, kConnectBackoffTimeoutEvent, kConnectFinalTimeoutEvent, kConnectRecdInputEvent, kConnectOutputDoneEvent, kConnectDisconnectEvent, kConnectChangeSpeed, // jdg kListenRecdInputEvent, kListenOutputDoneEvent, kListenDisconnectEvent, kListenChangeSpeed, // jdg kOutputUACommandEvent, kReplyTimeoutEvent, kReplyRecdInputEvent, kReplyOutputDoneEvent, kReplyDisconnectEvent, kReplyDiscoverRequestEvent, // jdg kReplyListenRequestEvent, // jdg kPriReceiveFinalTimeoutEvent, kPriReceiveRecdInputEvent, kPriReceiveOutputDoneEvent, kPriReceivePutDataEvent, kPriReceiveDisconnectEvent, kPriReceiveClrLocBsyPendEvent, kPriReceiveFRMREvent, kPriTransmitEnterEvent, kPriTransmitPollTimeoutEvent, kPriTransmitOutputDoneEvent, kPriTransmitPutDataEvent, kPriTransmitDisconnectEvent, kPriTransmitClrLocBsyPendEvent, kPriCloseFinalTimeoutEvent, kPriCloseRecdInputEvent, kPriCloseOutputDoneEvent, kPriCloseDisconnectRequest, kSecReceiveWDTimeoutEvent, kSecReceiveRecdInputEvent, kSecReceiveOutputDoneEvent, kSecReceivePutDataEvent, kSecReceiveDisconnectEvent, kSecReceiveClrLocBsyPendEvent, kSecReceiveConnLstnRequest, // jdg kSecTransmitEnterEvent, kSecTransmitOutputDoneEvent, kSecTransmitPutDataEvent, kSecTransmitDisconnectEvent, kSecTransmitClrLocBsyPendEvent, kSecTransmitConnLstnRequest, // jdg kSecCloseWDTimeoutEvent, kSecCloseRecdInputEvent, kSecCloseOutputDoneEvent, kSecCloseDisconnectRequest, // jdg kDiscoveredDevice, kDiscoverXIDCmdEvent, kDiscoverXIDRspEvent, kQOSSetBaudRateEvent, kQOSBufferInfoEvent, kQOSLeadInCountEvent, kQOSMinTurnAroundEvent, kQOSMaxTurnAroundEvent, kSetLocalBusyPendingEvent, kSetLocalBusyEvent, kAbortLocalBusyPendingEvent, kClrLocalBusyPendingEvent, kClrLocalBusyEvent, kNDMDisconnectFwdReply, kNDMDisconnectReply, kNRMDisconnectReply, kNRMDisconnectAsyncReply, kNRMDisconnectRequeue, kTestFrameReceivedEvent, kTestFrameOutputDoneEvent, kValidPacketReceived, kLogPacketDropped, kPacketOutput, kUnexpectedNr, // jdg kInvalidNr, kUnexpectedNs, kInvalidNs, kIgnoringInvalidNrNs, kOutputControlFrame, kOutputControlFrameRR, kOutputDataFrame, kOutputDataFrameNotFinal, kInputControlFrame, kInputControlFrameRR, kInputControlFrameNotFinal, kInputDataFrame, kInputDataFrameNotFinal, kResendRejectedFrames, kProcessISFrame, kUpdateNrReceived, kEnqueueEvent, // cpd kDequeueEventStart, kDequeueEventEnd, kReusingBuffer, kUsingDefaultBuffer, kLAPAddr, kInputAborted, // jdg performance hack kConnLstnComplete, kRejectRequest, // jdg - send back a request, kLogEnteringCloseState, kLogStartTimer, kLogStopTimer, kLogTimerComplete, kLogSuspend, kLogResume, kLogChangeSpeedCompleteIgnored, kLogMyAddr, kLogGotAddr, kLogStartInput, kLogStartInput2, kLogStartInput3, kLogGotData, kLogFailedFirst, kLogFailedSecond, kLogFailedThird, kLogReleaseInputBuffer, kLogReleaseInputBuffer2, kLogStartDataRcv1, kLogStartDataRcv2, kLogReset1, kLogReset2, kLogFreeGetBuffers1, kLogFreeGetBuffers2, kLogFreeGetBuffers3, kLogFreeGetBuffers4 }; static EventTraceCauseDesc TraceEvents[] = { {kLogCreate, "irlap: create obj="}, {kLogFree, "irlap: free obj="}, {kLogInit, "irlap: init, obj="}, {kLogInit2, "irlap: init, my dev addr="}, {kUnexpectedEvent, "irlap: unexpected event"}, {kLogStateEvent, "irlap: NextState, state=, event="}, {kNDMTimeoutEvent, "irlap: NDM media busy timer"}, {kNDMRecdInputEvent, "irlap: NDM recvd input"}, {kNDMDiscoveryEvent, "irlap: NDM discovery"}, {kNDMConnectEvent, "irlap: NDM connect"}, {kNDMListenEvent, "irlap: NDM listen"}, {kNDMDisconnectEvent, "irlap: NDM disconnect"}, {kQueryTimeoutEvent, "irlap: Probe query timer"}, {kQueryRecdInputEvent, "irlap: Probe query recvd input"}, {kQueryOutputDoneEvent, "irlap: Probe query output done"}, {kQueryDisconnectEvent, "irlap: Probe query disconnect"}, {kConnectBackoffTimeoutEvent, "irlap: Connect backoff timer"}, {kConnectFinalTimeoutEvent, "irlap: Connect F-timer"}, {kConnectRecdInputEvent, "irlap: Connect recvd input"}, {kConnectOutputDoneEvent, "irlap: Connect output done"}, {kConnectDisconnectEvent, "irlap: Connect disconnect"}, {kConnectChangeSpeed, "irlap: Connect changed speed"}, {kListenRecdInputEvent, "irlap: Listen recvd input"}, {kListenOutputDoneEvent, "irlap: Listen output done"}, {kListenDisconnectEvent, "irlap: Listen disconnect"}, {kListenChangeSpeed, "irlap: Listen changed spped"}, {kOutputUACommandEvent, "irlap: OutputUAPacket"}, {kReplyTimeoutEvent, "irlap: Probe reply timer"}, {kReplyRecdInputEvent, "irlap: Probe reply recvd input"}, {kReplyOutputDoneEvent, "irlap: Probe reply output done"}, {kReplyDisconnectEvent, "irlap: Probe reply disconnect"}, {kReplyDiscoverRequestEvent, "irlap: Probe reply, got discover request"}, {kReplyListenRequestEvent, "irlap: Probe reply, got listen request"}, {kPriReceiveFinalTimeoutEvent, "irlap: PRecv final timer"}, {kPriReceiveRecdInputEvent, "irlap: PRecv recvd input, cmd, Ns|Nr"}, {kPriReceiveOutputDoneEvent, "irlap: PRecv output done"}, {kPriReceivePutDataEvent, "irlap: PRecv put data"}, {kPriReceiveDisconnectEvent, "irlap: PRecv disconnect"}, {kPriReceiveClrLocBsyPendEvent, "irlap: PRecv clr local busy"}, {kPriReceiveFRMREvent, "irlap: PRecv recvd FRMR"}, {kPriTransmitEnterEvent, "irlap: PXmit enter"}, {kPriTransmitPollTimeoutEvent, "irlap: PXmit poll timer"}, {kPriTransmitOutputDoneEvent, "irlap: PXmit output done"}, {kPriTransmitPutDataEvent, "irlap: PXmit put data"}, {kPriTransmitDisconnectEvent, "irlap: PXmit disconnect"}, {kPriTransmitClrLocBsyPendEvent,"irlap: PXmit clr local busy"}, {kPriCloseFinalTimeoutEvent, "irlap: PClose final timer"}, {kPriCloseRecdInputEvent, "irlap: PClose recvd input"}, {kPriCloseOutputDoneEvent, "irlap: PClose output done"}, {kPriCloseDisconnectRequest, "irlap: PClose deferring disconnect request"}, {kSecReceiveWDTimeoutEvent, "irlap: SRecv WD timer"}, {kSecReceiveRecdInputEvent, "irlap: SRecv recvd input"}, {kSecReceiveOutputDoneEvent, "irlap: SRecv output done"}, {kSecReceivePutDataEvent, "irlap: SRecv put data"}, {kSecReceiveDisconnectEvent, "irlap: SRecv disconnect"}, {kSecReceiveClrLocBsyPendEvent, "irlap: SRecv clr local busy"}, {kSecReceiveConnLstnRequest, "irlap: SRecv conn/listen request"}, {kSecTransmitEnterEvent, "irlap: SXmit enter"}, {kSecTransmitOutputDoneEvent, "irlap: SXmit output done"}, {kSecTransmitPutDataEvent, "irlap: SXmit put data"}, {kSecTransmitDisconnectEvent, "irlap: SXmit disconnect"}, {kSecTransmitClrLocBsyPendEvent,"irlap: SXmit clr local busy"}, {kSecTransmitConnLstnRequest, "irlap: SXmit conn/listen request"}, {kSecCloseWDTimeoutEvent, "irlap: SClose WD timer"}, {kSecCloseRecdInputEvent, "irlap: SClose recvd input"}, {kSecCloseOutputDoneEvent, "irlap: SClose output done"}, {kSecCloseDisconnectRequest, "irlap: SClose deferring disconnect request"}, {kDiscoveredDevice, "irlap: discovered"}, {kDiscoverXIDCmdEvent, "irlap: put XID command"}, {kDiscoverXIDRspEvent, "irlap: put XID response"}, {kQOSSetBaudRateEvent, "irlap: new baud rate"}, {kQOSBufferInfoEvent, "irlap: buf cnt/size"}, {kQOSLeadInCountEvent, "irlap: lead in count"}, {kQOSMinTurnAroundEvent, "irlap: min turnaround"}, {kQOSMaxTurnAroundEvent, "irlap: max turnaround"}, {kSetLocalBusyPendingEvent, "irlap: set local busy pending"}, {kSetLocalBusyEvent, "irlap: set local busy"}, {kAbortLocalBusyPendingEvent, "irlap: set local busy pending aborted"}, {kClrLocalBusyPendingEvent, "irlap: clr local busy pending"}, {kClrLocalBusyEvent, "irlap: clr local busy"}, {kNDMDisconnectFwdReply, "irlap: NDM disconnect forward reply"}, {kNDMDisconnectReply, "irlap: NDM disconnect reply"}, {kNRMDisconnectReply, "irlap: NRM disconnect reply"}, {kNRMDisconnectAsyncReply, "irlap: NRM disconnect async reply"}, {kNRMDisconnectRequeue, "irlap: NRM disconnect requeue event"}, {kTestFrameReceivedEvent, "irlap: test frame received"}, {kTestFrameOutputDoneEvent, "irlap: test frame response sent"}, {kValidPacketReceived, "irlap: valid packet received"}, {kLogPacketDropped, "irlap: packet dropped ** no read pending!"}, {kPacketOutput, "irlap: packet output"}, {kUnexpectedNr, "irlap: Unexpected NR"}, // jdg {kInvalidNr, "irlap: Invalid NR"}, {kUnexpectedNs, "irlap: Unexpected NS"}, {kInvalidNs, "irlap: Invalid NS"}, {kIgnoringInvalidNrNs, "irlap: IGNORING*** invalid NrNs"}, {kOutputControlFrame, "irlap: output non-RR control frame, Cmd, Nr"}, {kOutputControlFrameRR, "irlap: output RR control frame, 0, Nr"}, {kOutputDataFrame, "irlap: output data frame (Ns, Nr)"}, {kOutputDataFrameNotFinal, "irlap: output data frame, not final (Ns,Nr)"}, {kInputControlFrame, "irlap: input NON-RR control frame, (CMD,Nr)"}, {kInputControlFrameRR, "irlap: input RR control frame, (0, Nr)"}, {kInputControlFrameNotFinal, "irlap: input control frame, NOT FINAL (cfield,Nr)"}, {kInputDataFrame, "irlap: input data frame (Ns, Nr)"}, {kInputDataFrameNotFinal, "irlap: input data frame, non-final (Ns, Nr)"}, {kResendRejectedFrames, "irlap: inside resend rejected frames"}, {kProcessISFrame, "irlap: process Info/Super frame"}, {kUpdateNrReceived, "irlap: update Nr received"}, {kEnqueueEvent, "irlap: Event Queued"}, {kDequeueEventStart, "irlap: Event Start"}, {kDequeueEventEnd, "irlap: Event End"}, {kReusingBuffer, "irlap: start data recv, reusing input buffer"}, {kUsingDefaultBuffer, "irlap: start data recv, using DEFAULT input buffer"}, {kLAPAddr, "irlap: Peer Addr: My Addr:"}, {kInputAborted, "irlap: input abort - fast link turnaround after read err"}, {kConnLstnComplete, "irlap: conn/lstn complete, peer dev addr="}, {kRejectRequest, "irlap: rejecting request"}, {kLogEnteringCloseState, "irlap: entering close state at #"}, {kLogStartTimer, "irlap: start timer, (delay, event)"}, {kLogStopTimer, "irlap: stop timer"}, {kLogTimerComplete, "irlap: timer complete, 0, event"}, {kLogSuspend, "irlap: sleep"}, {kLogResume, "irlap: wakeup"}, {kLogChangeSpeedCompleteIgnored, "irlap: change speed complete ignored"}, {kLogMyAddr, "irlap: my addr"}, {kLogGotAddr, "irlap: rcvd addr"}, {kLogStartInput, "irlap: start input, buf="}, {kLogStartInput2, "irlap: start input, old buf="}, {kLogStartInput3, "irlap: start input - keeping memory from leaking"}, {kLogGotData, "irlap: gotdata, wanted=, got="}, {kLogFailedFirst, "irlap: ** failed ** 1st test"}, {kLogFailedSecond, "irlap: ** failed ** second test"}, {kLogFailedThird, "irlap: ** failed ** third test"}, {kLogReleaseInputBuffer, "irlap: release input buffer, buffer="}, {kLogReleaseInputBuffer2, "irlap: release input buffer, flags, index"}, {kLogStartDataRcv1, "irlap: start data rcv alloc, buffer="}, {kLogStartDataRcv2, "irlap: start data rcv alloc, flags, index"}, {kLogReset1, "irlap: reset, buffer alloc mask"}, {kLogReset2, "irlap: reset, states"}, {kLogFreeGetBuffers1, "irlap: free get buffers, buffer="}, {kLogFreeGetBuffers2, "irlap: free get buffers, flag, index"}, {kLogFreeGetBuffers3, "irlap: free get buffers, mask="}, {kLogFreeGetBuffers4, "irlap: free get buffers, found fInputBuffer"} }; #define XTRACE(x, y, z) IrDALogAdd ( x, y, z, TraceEvents, true ) #else #define XTRACE(x, y, z) ((void)0) #endif // hasTracing && hasLAPTracing #define GetLMP (fIrDA->GetLMP()) #define GetDiscovery (fIrDA->GetDiscovery()) #define GetIrDevice (fIrDA->GetIrDevice()) void LAPTimerNotifier( UInt32 refCon, UInt32 sig ); Boolean gLastWasFRMR = false; // TESTING. Try (once) to recover from rcvd FRMR (shouldn't happen) //============================ Initialization stuff ============================== #define super TIrStream OSDefineMetaClassAndStructors(TIrLAP, TIrStream); //-------------------------------------------------------------------------------- // TIrLAP //-------------------------------------------------------------------------------- /*static*/ TIrLAP * TIrLAP::tIrLAP(TIrGlue *irda, TIrQOS *myQOS, TIrQOS* peerQOS) { TIrLAP *obj = new TIrLAP; XTRACE(kLogCreate, (int)obj >> 16, (short)obj); if (obj && !obj->Init(irda, myQOS, peerQOS)) { obj->release(); obj = nil; } return obj; } Boolean TIrLAP::Init(TIrGlue *irda, TIrQOS *myQOS, TIrQOS* peerQOS) { XTRACE(kLogInit, (int)this >> 16, (short)this); fState = kIrLAPDisconnectedState; // maybe should have an invalid state here fConnAddr = 0; fMyDevAddr = 0; fDiscoverMaxSlots = 0; fDiscoverSlot = 0; fDiscoverFlags =0; fDiscoverReplied = fDiscoverEnteredReplyState = false; fConflictDevAddr = fReplacementDevAddr = fPeerDevAddr= 0; fCurrentRequest = nil; fPendingDisconnect = nil; fPendingRequests = nil; //fMyQOS = fPeerQOS = nil; fMyQOS = myQOS; fPeerQOS = peerQOS; fVr = fVs = fNextToAck = fWindow = 0; fConnected = fLocalBusy = fRemoteBusy = false; fSetLocalBusyPending = fClrLocalBusyPending = fEnteringCloseState = fRespondingToDisconnect = false; fWaitingForPollTimer = fHandlingTestFrame = false; bzero(&fTestHeader, sizeof(fTestHeader)); fFRMRPending = false; fFRMRRejCtrlField = fFRMRMyNrAndNs = fFRMRReasonFlags = 0; fRetryCount = fDisconnectWarningLimit = fDisconnectLinkLimit = 0; fInitialRetryTime = fDisconnectLinkLimitTime = fBusyCounter = 0; fDataRetries = fProtocolErrs = 0; fLocalBusyClearedEvent = nil; fLeadInCount = fMyWindowSize = fPeerWindowSize = 0; fPollTimerTimeout = fFinalTimerTimeout = fWatchdogTimeout = fMinTurnAroundTimeout = 0; fPrimary = fPutReqsPending = false; fNextCmdRspToSend = fLastCmdRsp = 0; fRecdCtrl = fRecdCR = fRecdAddr = 0; fRecdPF = fRecdNr = fRecdNs = fRecdCmdRsp = 0; fValidRecdNr = fValidRecdNs = fNrNsFlags = 0; fIOBufferItem = nil; fInputInProgress = fOutputInProgress = fInBrokenBeam = false; fGetBufferAvail = fNumGetBuffers = 0; bzero(fGetBuffers, sizeof(fGetBuffers)); fInputBuffer = nil; bzero(fNickName, sizeof(fNickName)); fPendingPutRequests = nil; fPutBuffer = nil; bzero(fPutRequests, sizeof(fPutRequests)); #if (hasTracing > 0 && hasLAPTracing > 0) if (!super::Init(irda, TraceEvents, kEnqueueEvent)) return false; #else if (!super::Init(irda)) return false; #endif fLeadInCount = kIrDefaultLeadinCount; //fNeedNewInputBuffer = true; // Get a random device address for my address (range is 1 - FFFFFFFE) fMyDevAddr = ( random() % 0xFFFFFFFE + 1 ); // Make it something different if it happens to be the sniff or probe addresses if ((fMyDevAddr == kIrLAPSnifferDevAddr) || (fMyDevAddr == kIrLAPSniffeeDevAddr) || (fMyDevAddr == kIrLAPProbeDevAddr)) { fMyDevAddr++; } XTRACE(kLogInit2, fMyDevAddr>>16, fMyDevAddr); require(fMyDevAddr, Fail); // should not be zero fIOBufferItem = CBufferSegment::New(3000); // Fix (size is ignored for now) require(fIOBufferItem, Fail); fPendingPutRequests = CList::cList(); require(fPendingPutRequests, Fail); fPendingRequests = CList::cList(); require(fPendingRequests, Fail); fLocalBusyClearedEvent = fIrDA->GrabEventBlock(kIrLocalBusyClearedEvent, sizeof(TIrEvent)); // review, needed? require(fLocalBusyClearedEvent, Fail); fPutBuffer = TIrLAPPutBuffer::tIrLAPPutBuffer(); require(fPutBuffer, Fail); ResetStats(); // reset stats counters (needed?) return true; Fail: return false; } // TIrLAP::Init //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIrLAP::free() { XTRACE(kLogFree, (int)this >> 16, (short)this); #define FREE(x) { if (x) { (x)->release(); x = nil; } } // Free any buffer lists that have been allocated FreeGetBuffers(); FREE(fPendingPutRequests); FREE(fPendingRequests); FREE(fIOBufferItem); FREE(fPutBuffer); if (fIrDA) { int Review_Event_Free; // may be able to delete this //if (fLocalBusyEvent) { // fIrDA->ReleaseEventBlock(fLocalBusyEvent); // fLocalBusyEvent = nil; //} if (fLocalBusyClearedEvent) { fIrDA->ReleaseEventBlock(fLocalBusyClearedEvent); fLocalBusyClearedEvent = nil; } } super::free(); } // TIrLAP::free //-------------------------------------------------------------------------------- // Reset //-------------------------------------------------------------------------------- void TIrLAP::Reset() { // called by our disconnect complete, and via the glue async disconnect // and also by glue's stop code (so state can be other than disconnected) XTRACE(kLogReset1, fGetBufferAvail >> 16, fGetBufferAvail); XTRACE(kLogReset2, kIrLAPDisconnectedState, fState); //check(fState == kIrLAPDisconnectedState); // also kIrLAPPriCloseState and ?? fState = kIrLAPDisconnectedState; FreeGetBuffers(); } // TIrLAP::Reset // Sleep/wakeup support void TIrLAP::Suspend(void) { XTRACE(kLogSuspend, 0, 0); fRetryCount = fDisconnectLinkLimit; // force disconnect on next timeout } void TIrLAP::Resume(void) { XTRACE(kLogResume, 0, 0); } //-------------------------------------------------------------------------------- // GetNickName //-------------------------------------------------------------------------------- void TIrLAP::GetNickName( UInt8 * nickName ) { if( fNickName[0] != 0 ) strcpy( ( char * )nickName, ( const char * )fNickName ); else nickName[0] = 0; } //-------------------------------------------------------------------------------- // FreeGetBuffers //-------------------------------------------------------------------------------- void TIrLAP::FreeGetBuffers() { UInt32 flag, index; XTRACE(kLogFreeGetBuffers3, fGetBufferAvail >> 16, (short)fGetBufferAvail); // Free the input buffers (allocated during connect with peer) for (index = 0, flag=1; index < fNumGetBuffers; index++, flag <<= 1) { XTRACE(kLogFreeGetBuffers1, (int)fGetBuffers[index] >> 16, (short)fGetBuffers[index]); XTRACE(kLogFreeGetBuffers2, flag, index); check(fGetBuffers[index]); check((fGetBufferAvail & flag) || // should be "available" OR (fInputBuffer == fGetBuffers[index])); // current fInputBuffer if (fGetBuffers[index] && (fGetBufferAvail & flag)) { // if ok to delete fGetBuffers[index]->Delete(); // release the buffer } else { // if it's in use as fInputBuffer then it won't have it's avail flag set // but we can free it anyway since we own it (wasn't sent up the stack) if (fInputBuffer == fGetBuffers[index]) { XTRACE(kLogFreeGetBuffers4, (int)fInputBuffer >> 16, (short)fInputBuffer); fInputBuffer = nil; fGetBuffers[index]->Delete(); // release the buffer } else { // memory leak, but let's not free it since it may be in use DebugLog("memory leak! fInputBuffer=0x%lx\n", (UInt32)fInputBuffer); DebugLog("buffer problem in free get buffers, index %ld, buffer 0x%lx\n", index, (UInt32) fGetBuffers[index]); //IrDALogTracingOff(); } } fGetBuffers[index] = nil; // sanity } fNumGetBuffers = 0; // no longer have any buffers available fGetBufferAvail = 0; // bitmask of available buffers is zero available now } // TIrLAP::FreeGetBuffers // // JDG: lapconn needs a way to abort a listen if a connect request comes in // while a listen is pending. This will do it if we're still in NDM state // Could avoid this if we didn't use client event records for lapconn/lap interaction // TIrEvent * TIrLAP::CancelPendingListenRequest(void) // return event if listen canceled { TIrEvent *request = GetCurrentEvent(); StopInput(); // stop input, lapconn wants to stop the listen if (fState == kIrLAPDisconnectedState && request && request->fEvent == kIrListenRequestEvent) { //RejectRequest(request, kIRErrRetry); // no, don't send it back fCurrentRequest = nil; // no pending request now return request; // return 'ok' } return nil; } // TIrLAP::CancelPendingListenRequest //-------------------------------------------------------------------------------- // NextState //-------------------------------------------------------------------------------- void TIrLAP::NextState(ULong event) { XTRACE(kLogStateEvent, fState, event); switch (fState) { case kIrLAPDisconnectedState: HandleDisconnectedStateEvent(event); break; case kIrLAPQueryState: HandleQueryStateEvent(event); break; case kIrLAPConnectState: HandleConnectStateEvent(event); break; case kIrLAPListenState: HandleListenStateEvent(event); break; case kIrLAPReplyState: HandleReplyStateEvent(event); break; case kIrLAPPriReceiveState: HandlePriReceiveStateEvent(event); break; case kIrLAPPriTransmitState: HandlePriTransmitStateEvent(event); break; case kIrLAPPriCloseState: HandlePriCloseStateEvent(event); break; case kIrLAPSecReceiveState: HandleSecReceiveStateEvent(event); break; case kIrLAPSecTransmitState: HandleSecTransmitStateEvent(event); break; case kIrLAPSecCloseState: HandleSecCloseStateEvent(event); break; default: DebugLog("TIrLAP::NextState: bad fState"); break; } } //-------------------------------------------------------------------------------- // HandleDisconnectedStateEvent (fState == kIrLAPDisconnectedState) NDM //-------------------------------------------------------------------------------- void TIrLAP::HandleDisconnectedStateEvent(ULong event) { #if forMac // Using a hammer for the problem, but if I am fConnected = false; // here, then I am disconnected. Simple logic fDiscoverActive = false; // If I am discovering, then it will get set to //gConnectionBroken = false; // true before I exit. fNickName[0] = 0; // Clear out the connect name. #endif switch (event) { case kIrDiscoverRequestEvent: { XTRACE(kNDMDiscoveryEvent, 0, 0); fDiscoverActive = true; // flag for status routines that we're discovering // Save the request block - use it as the reply block later //XASSERT(fCurrentRequest == nil); // jdg: maybe on the newton if (fCurrentRequest != nil) { // JDG ADDED, can we do better? RejectRequest(fCurrentRequest, kIrDAErrRetry); // dispose of previous request } fCurrentRequest = GetCurrentEvent(); // Start up the media busy timeout timer GetIrDevice->ResetMediaBusy(); StartTimer(kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent); fBusyCounter = 0; // JDG: Try N times to discover // StartMediaBusyTimer( kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent ); // Wait for possible xid request from peer device StartInput(fIOBufferItem); } break; case kIrConnectRequestEvent: { XTRACE(kNDMConnectEvent, 0, 0); TIrConnLstnRequest* connectRequest = (TIrConnLstnRequest*)GetCurrentEvent(); // Save the request block - use it as the reply block later //XASSERT(fCurrentRequest == nil); // jdg - maybe on a newton if (fCurrentRequest != nil) { // JDG ADDED, can we do better? fDiscoverActive = false; // we're not discovering anymore StopTimer(); // stop media timer (if discovery req) RejectRequest(fCurrentRequest, kIrDAErrRetry); // dispose of previous request } fCurrentRequest = GetCurrentEvent(); // Save myQOS parms and place to receive peerQOS parms fMyQOS = connectRequest->fMyQOS; fPeerQOS = connectRequest->fPeerQOS; /*** TEMP DEBUGGING ***/ if (1) { // First my info XTRACE(kQOSBufferInfoEvent, fMyQOS->GetWindowSize(), fMyQOS->GetDataSize()); XTRACE(kQOSLeadInCountEvent, 0, fMyQOS->GetExtraBOFs()); XTRACE(kQOSMinTurnAroundEvent, 0, fMyQOS->GetMinTurnAroundTime()); XTRACE(kQOSMaxTurnAroundEvent, 0, fMyQOS->GetMaxTurnAroundTime()); // Then the peer device's info XTRACE(kQOSBufferInfoEvent, fPeerQOS->GetWindowSize(), fPeerQOS->GetDataSize()); XTRACE(kQOSLeadInCountEvent, 1, fPeerQOS->GetExtraBOFs()); XTRACE(kQOSMinTurnAroundEvent, 1, fPeerQOS->GetMinTurnAroundTime()); XTRACE(kQOSMaxTurnAroundEvent, 1, fPeerQOS->GetMaxTurnAroundTime()); } /**** TEMP DEBUGGING ****/ // Address of device to connect to fPeerDevAddr = connectRequest->fDevAddr; // Get a random connection address for the conn address to be used while in NRM mode // The IrLAP spec reserves values 0 and 7F, but some errata have also implied // that 1 and 7E are also invalid, so I'm leaving them out too to be safe. fConnAddr = (UByte)random() % 0x7C + 2; // 0-7B plus 2 => 2-7D // For efficiency, avoid conn addrs of 0x60 (which become 0xC0/0xC1 in A field) // and 0x3E (which become 0x7D in A field). Otherwise the A field would always // have to be escaped. This avoids it up front, since we have control in this case. // NOTE: This is a SIR only issue, but doesn't hurt if also done for FIR. if ((fConnAddr == 0x60) || (fConnAddr == 0x3E)) { // Now 0x60 => 0x61 (0xC2/0xC3) and 0x3E => 0x3F (0x7E); and no escaping needed. fConnAddr++; } XTRACE( kLAPAddr, fPeerDevAddr, fConnAddr ); check(fConnAddr); GetIrDevice->SetLAPAddress( fConnAddr ); // Init some state fRetryCount = 0; fInitialRetryTime = 0; // Change state to connect(ing) fState = kIrLAPConnectState; // Begin by sending the SNRM command OutputSNRMCommand(); } break; case kIrListenRequestEvent: { XTRACE(kNDMListenEvent, 0, 0); TIrConnLstnRequest* listenRequest = (TIrConnLstnRequest*)GetCurrentEvent(); // Save the request block - use it as the reply block later //XASSERT(fCurrentRequest == nil); // jdg: maybe on a newton // Listen requests defer to pending discover, reject this one if we're not idle if (fCurrentRequest != nil) { // Can we do better? //StopTimer(); // don't stop media timer //RejectRequest(fCurrentRequest, kIrDAErrCancel); // dispose of previous request RejectRequest(GetCurrentEvent(), kIrDAErrRetry); // reject the new LISTEN (discover in progress) return; // and return! } fCurrentRequest = GetCurrentEvent(); // Save myQOS parms and place to receive peerQOS parms fMyQOS = listenRequest->fMyQOS; fPeerQOS = listenRequest->fPeerQOS; /////////////////////////////////////////////////////////////////////// // TESTING ABORT ASYNC I/O HACK ///fDiscoverActive = false; // we are not discovering, thank you ///StartTimer(5*1000, kIrMediaBusyTimerExpiredEvent); // 5 seconds /////////////////////////////////////////////////////////////////////// // Begin by waiting for initial XID command // (to be followed by (optional addr resolutions) then a snrm) StartInput(fIOBufferItem); } break; case kIrInputCompleteEvent: { XTRACE(kNDMRecdInputEvent, fRecdCmdRsp, fRecdAddr); // Stop the media busy timer (if we are discovering) StopTimer(); // Check for xid (peer device is discovering) if (RecdPollCmd(kIrLAPCmdXID)) { fDiscoverEnteredReplyState = true; HandleReplyStateEvent(kIrInputCompleteEvent); // Note: input was reposted in HandleReplyStateEvent (if didn't change state) } // Check for snrm (peer device is connecting) else if (RecdPollCmd(kIrLAPCmdSNRM)) { HandleListenStateEvent(kIrInputCompleteEvent); // Note: input was reposted by HandleListenStateEvent (if didn't change state) } // It was something else. Ignore it, but repost the input else { StartInput(fIOBufferItem); } // If we are still in the disconnected state (because the XID wasn't // for us or the snrm wasn't for us) then restart/continue the requests if (fState == kIrLAPDisconnectedState) { //XASSERT(fCurrentRequest != nil); // Note: If listen almost completed but failed with an out of memory // error then fCurrentRequest could legitimately be nil at this point. if (fCurrentRequest && fCurrentRequest->fEvent == kIrDiscoverRequestEvent) { // Start up the media busy timeout timer GetIrDevice->ResetMediaBusy(); StartTimer(kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent); // StartMediaBusyTimer( kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent ); } } } break; case kIrMediaBusyTimerExpiredEvent: { // DebugLog("LAP NDM kIrMediaBusyTimerExpiredEvent" ); XTRACE(kNDMTimeoutEvent, 0, 0); //XASSERT((fCurrentRequest != nil) && (fCurrentRequest->fEvent == kIrDiscoverRequestEvent)); TIrDiscoverRequest* discoverRequest = (TIrDiscoverRequest*)fCurrentRequest; fDiscoverActive = true; // this should already be set! // Check mediaBusy flag to really make sure that there was no "traffic" if (GetIrDevice->GetMediaBusy()) { // Media is busy, wait some more before trying again // NOTE: This implementation currently tries forever. If that is not the correct // model then need to use a retry count here and return error when retry is reached. /*** JDG: Let's stop the discover after one try. ... nope, now N tries ****/ if (fBusyCounter++ < kMaxDiscoverRetries) { // if ok to try again //StopInput(); // jdg: at least reset the busy flag! //StartInput(fIOBufferItem); // jdg: by stopping & restarting read GetIrDevice->ResetMediaBusy(); // reset busy flag directly now StartTimer(kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent); // StartMediaBusyTimer( kIrMediaBusyTimeout, kIrMediaBusyTimerExpiredEvent ); } else { // give up and reject the discover request fDiscoverActive = false; // jdg: don't try again discoverRequest->fPassiveDiscovery = false; // probably not needed, but ... RejectRequest(fCurrentRequest, kIrDAErrLinkBusy); // return the request fCurrentRequest = nil; } } // No other traffic - okay to begin a discover dialog else { StopInput(); // Default to 1 slot if invalid numSlots is passed in fDiscoverFlags = kIrLAPDiscoverFlags1Slot; fDiscoverMaxSlots = 0; // Initially 1 - 1 // Assign appropriate slot bit field UByte index; for (index = 0; index < sizeof(IrSlotCounts); index++) { if (IrSlotCounts[index] == discoverRequest->fNumSlots) { fDiscoverFlags = index; fDiscoverMaxSlots = (UByte)discoverRequest->fNumSlots - 1; break; } } // Set fConflictDevAddr (which may be kIrLAPBroadcastDevAddr) if discover vs addr conflict fConflictDevAddr = discoverRequest->fConflictDevAddr; if (fConflictDevAddr != kIrLAPBroadcastDevAddr) { fDiscoverFlags |= kIrLAPDiscoverFlagsNewAddr; } // Start from the beginning fDiscoverSlot = 0; // Change state to discover(ing) fState = kIrLAPQueryState; // Begin by sending the initial XID command OutputXIDCommand(); } } break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrDisconnectRequestEvent: XTRACE(kNDMDisconnectEvent, 0, 0); HandleNDMDisconnectRequest(); break; case kIrPutDataRequestEvent: NotConnectedCompletion(); // jdg: yes! send back to CIrLSAP //RejectRequest(GetCurrentEvent(), kCommErrNotConnected); // jdg: send to lmp, not directly to lsapconn break; case kIrLocalBusyClearedEvent: // Don't need to do anything here. // This is a side effect of releasing buffers at disconnect cleanup (see IrLAPConn). break; case kIrOutputCompleteEvent: // jdg added. this can be safely ignored, we're on the way break; // out ... I hope! default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleDisconnectedStateEvent: bad event"); break; } } // TIrLAP::HandleDisconnectedStateEvent //-------------------------------------------------------------------------------- // HandleQueryStateEvent (fState == kIrLAPQueryState) //-------------------------------------------------------------------------------- void TIrLAP::HandleQueryStateEvent(ULong event) { fDiscoverActive = true; // Always update date the globals. switch (event) { case kIrInputCompleteEvent: XTRACE(kQueryRecdInputEvent, fRecdCmdRsp, fRecdAddr); // Only accept broadcast XID responses if ((fRecdAddr == kIrLAPBroadcastAddr) && RecdFinalRsp(kIrLAPRspXID)) { TXIDPacket xidRsp; // Parse the received info if (GotData(&xidRsp.fFormatId, kTXIDPacketSize - 2)) { // Only log it if dest address is mine if (xidRsp.fDstDevAddr == fMyDevAddr) { XTRACE(kDiscoveredDevice, 0, (xidRsp.fSrcDevAddr >> 16)); XTRACE(kDiscoveredDevice, 1, (xidRsp.fSrcDevAddr)); // Log the discovered info (rev/addr/info) TIrDscInfo* discoveryInfo = TIrDscInfo::tIrDscInfo(); if (discoveryInfo != nil) { TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)fCurrentRequest; XASSERT(discoverReply != nil); // Fill in, extract info for discover info discoveryInfo->SetVersion(xidRsp.fVersion); discoveryInfo->SetDeviceAddr(xidRsp.fSrcDevAddr); discoveryInfo->ExtractDevInfoFromBuffer(fInputBuffer); // Add it to the list of discovered devices discoverReply->fDiscoveredDevices->InsertLast(discoveryInfo); } } else { XTRACE(kLogMyAddr, fMyDevAddr >> 16, (short)fMyDevAddr); XTRACE(kLogGotAddr, xidRsp.fDstDevAddr >> 16, (short)xidRsp.fDstDevAddr); XTRACE(kLogFailedThird, 0, 0); DebugLog(" failed 3rd"); } } else { DebugLog(" failed 2nd"); XTRACE(kLogFailedSecond, 0, 0); } } else { XTRACE(kLogFailedFirst, 0, 0); DebugLog(" failed 1st"); } // Wait for input StartInput(fIOBufferItem); break; case kIrOutputCompleteEvent: XTRACE(kQueryOutputDoneEvent, fDiscoverSlot, fDiscoverMaxSlots); XASSERT(fNextCmdRspToSend == kIrLAPCmdXID); if (fDiscoverSlot != kIrLAPFinalSlot) { // Start the slot timer and wait for a reply StartTimer(kIrDiscoverSlotTimeout, kIrSlotTimerExpiredEvent); //#if forMac StartInput(fIOBufferItem); // issue read only if we're maybe expecting something //#endif } else { // Let the initiator know that the discovery phase has completed TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)fCurrentRequest; fCurrentRequest = nil; discoverReply->fEvent = kIrDiscoverReplyEvent; discoverReply->fPassiveDiscovery = false; #if forMac fDiscoverActive = false; // Update the globals. /////////////////////////////////////////////////////////////////////// // TESTING ABORT ASYNC I/O HACK // no we're hanging a read here before going back to disconnect state //StartTimer(5*1000, kIrMediaBusyTimerExpiredEvent); // 5 seconds /////////////////////////////////////////////////////////////////////// #endif // Discovery process complete fState = kIrLAPDisconnectedState; GetLMP->EnqueueEvent(discoverReply); } //#if forMac // StartInput(fIOBufferItem); // don't do a read after the last XID, no reply coming //#endif break; case kIrSlotTimerExpiredEvent: XTRACE(kQueryTimeoutEvent, fDiscoverSlot, fDiscoverMaxSlots); StopInput(); if (fDiscoverSlot < fDiscoverMaxSlots) { // Send out XID for next slot fDiscoverSlot++; } else { // Send out final XID fDiscoverSlot = kIrLAPFinalSlot; } OutputXIDCommand(); break; case kIrDisconnectRequestEvent: XTRACE(kQueryDisconnectEvent, 0, 0); XASSERT(fCurrentRequest != nil); #if forMac fDiscoverActive = false; // Update the globals. #endif HandleNDMDisconnectRequest(); break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleQueryStateEvent: bad event"); break; } } // TIrLAP::HandleQueryStateEvent //-------------------------------------------------------------------------------- // HandleConnectStateEvent (fState == kIrLAPConnectState) //-------------------------------------------------------------------------------- void TIrLAP::HandleConnectStateEvent(ULong event) { switch (event) { case kIrChangeSpeedCompleteEvent: XTRACE(kConnectChangeSpeed, 0, 0); // log it OutputControlFrame(kIrLAPFrameRR); // Note: OutputControlFrame sets poll bit break; case kIrInputCompleteEvent: { XTRACE(kConnectRecdInputEvent, fRecdCmdRsp, 0); Boolean repostInput = true; if (RecdFinalRsp(kIrLAPRspUA)) { TUAPacket uaRsp; // Parse the received ua response if (GotData((UByte*)&uaRsp.fSrcDevAddr, kTUAPacketSize - 2)) { if ((uaRsp.fSrcDevAddr == fPeerDevAddr) && (uaRsp.fDstDevAddr == fMyDevAddr)) { IrDAErr result; StopTimer(); // Parse, negotiate and init conn state (and apply time consuming QOS parms) result = ParseNegotiateAndInitConnState(true /*primary*/); // NOTE: If error, ConnLstnComplete has already been called and gone to NDM if (result != noErr) break; // Change baud rate to the negotiated speed GetIrDevice->ChangeSpeed(fMyQOS->GetBaudRate()); // Note that leadInCount is not changed yet on purpose // so there is a minimum turnaround before sending the RR // MOVED next line to after change speed completes //OutputControlFrame(kIrLAPFrameRR); // Note: OutputControlFrame sets poll bit repostInput = false; #if forMac // Report I are connected, though not fConnected = true; // really until I receive an RR from // the other size. GetDiscovery->GetRemoteDeviceName( fPeerDevAddr, fNickName ); #endif } } } else if (RecdPollCmd(kIrLAPCmdSNRM)) { TSNRMPacket snrmCmd; // Parse the received snrm command if (GotData((UByte*)&snrmCmd.fSrcDevAddr, kTSNRMPacketSize - 2)) { // NOTE: Only agree to be secondary if myDevAddr < peerDevAddr if ((snrmCmd.fDstDevAddr == fMyDevAddr) && (fMyDevAddr < snrmCmd.fSrcDevAddr)) { IrDAErr result; UByte connAddr = snrmCmd.fConnAddr >> 1; // Don't accept any wooden nickels (whatever that means) if ((connAddr > 0) && (connAddr < kIrLAPBroadcastAddr)) { // Recd snrm while connecting and myDevAddr < peerDevAddr - so enter listen state fState = kIrLAPListenState; // Save relevent info fConnAddr = connAddr; fPeerDevAddr = snrmCmd.fSrcDevAddr; StopTimer(); //check(fPeerDevAddr); //fIrSIR->SetLAPAddress( fPeerDevAddr ); check(connAddr); // JDG: let's use the right GetIrDevice->SetLAPAddress(connAddr); // field, shall we? // Parse, init conn state and apply time consuming QOS parms result = ParseNegotiateAndInitConnState(false /*primary*/); // NOTE: If error, ConnLstnComplete has already been called and gone to NDM if (result != noErr) break; OutputUAResponse(); repostInput = false; } } } } else if (RecdCmd(kIrLAPCmdDISC) || RecdRsp(kIrLAPRspDM)) { // Let the initiator know that the connect failed - (was stopped by peer) ConnLstnComplete(kIrDAErrCancel); repostInput = false; } // Resume input if nothing recognizable was received if (repostInput) { StartInput(fIOBufferItem); } } break; case kIrOutputCompleteEvent: XTRACE(kConnectOutputDoneEvent, fNextCmdRspToSend, fConnAddr); switch (fNextCmdRspToSend) { case kIrLAPCmdSNRM: fRetryCount++; StartTimer(kIrConnectFinalTimerTimeout, kIrFinalTimerExpiredEvent); StartInput(fIOBufferItem); break; case kIrLAPFrameRR: // Start receiving at earliest possible point //StartDataReceive(); // jdg: can't do this since read can call me back w/data // Set additional number of BOFs required // NOTE: Done after sending RR so there is a min turnaround delay (w/BOFs) fLeadInCount = (UByte)fPeerQOS->GetExtraBOFs(); // Let the initiator know that the connect succeeded ConnLstnComplete(noErr); StartTimer(fFinalTimerTimeout, kIrFinalTimerExpiredEvent); fState = kIrLAPPriReceiveState; StartDataReceive(); // now that state's changed, can do a read break; default: DebugLog("TIrLAP::HandleConnectStateEvent: bad output state"); break; } break; case kIrFinalTimerExpiredEvent: XTRACE(kConnectFinalTimeoutEvent, 0, 0); if (fRetryCount < kMaxConnectRetries) { // Delay random time before resending SNRM // Random delay x should be between .5 and 1.5 time to send SNRM request // Time to send SNRM is appx 50 mSecs, range of x is 25 <= x <= 75 mSecs StartTimer((25 + (random() % 50)) * kMilliseconds, kIrBackoffTimerExpiredEvent); } else { StopInput(); // Let the initiator know that the connect failed - no responder ConnLstnComplete(kIrDAErrTimeout); } break; case kIrBackoffTimerExpiredEvent: XTRACE(kConnectBackoffTimeoutEvent, 0, 0); StopInput(); OutputSNRMCommand(); break; case kIrDisconnectRequestEvent: XTRACE(kConnectDisconnectEvent, 0, 0); XASSERT(fCurrentRequest != nil); HandleNDMDisconnectRequest(); break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleConnectStateEvent: bad event"); break; } } // TIrLAP::HandleConnectStateEvent //-------------------------------------------------------------------------------- // HandleListenStateEvent (fState == kIrLAPListenState) //-------------------------------------------------------------------------------- void TIrLAP::HandleListenStateEvent(ULong event) { switch (event) { case kIrInputCompleteEvent: { XTRACE(kListenRecdInputEvent, fRecdCmdRsp, 0); Boolean repostInput = true; if (RecdPollCmd(kIrLAPCmdSNRM)) { TSNRMPacket snrmCmd; // Parse the received snrm command if (GotData((UByte*)&snrmCmd.fSrcDevAddr, kTSNRMPacketSize - 2)) { if (snrmCmd.fDstDevAddr == fMyDevAddr) { IrDAErr result; UByte connAddr = snrmCmd.fConnAddr >> 1; // Don't accept any wooden nickels (whatever that means) if ((connAddr > 0) && (connAddr < kIrLAPBroadcastAddr)) { // Recd valid snrm for me - so enter listen state fState = kIrLAPListenState; // Save relevent info fConnAddr = connAddr; fPeerDevAddr = snrmCmd.fSrcDevAddr; //check(fPeerDevAddr); //fIrSIR->SetLAPAddress( fPeerDevAddr ); check(connAddr); // JDG: let's use the right GetIrDevice->SetLAPAddress(connAddr); // field, shall we? // Parse, init conn state and apply time consuming QOS parms result = ParseNegotiateAndInitConnState(false /*primary*/); // NOTE: If error, ConnLstnComplete has already been called and gone to NDM if (result != noErr) break; OutputUAResponse(); repostInput = false; } } } } // Resume input if nothing recognizable was received if (repostInput) { StartInput(fIOBufferItem); } } break; case kIrOutputCompleteEvent: XTRACE(kListenOutputDoneEvent, fNextCmdRspToSend, 0); XASSERT(fNextCmdRspToSend == kIrLAPRspUA); // Change baud rate to the negotiated speed GetIrDevice->ChangeSpeed(fMyQOS->GetBaudRate()); // "falls through" after ChangeSpeed completes .. jdg 6/26/2000 break; case kIrChangeSpeedCompleteEvent: XTRACE(kListenChangeSpeed, 0, 0); // Start receiving at earliest possible point //StartDataReceive(); // jdg: moved below // Set additional number of BOFs required fLeadInCount = (UByte)fPeerQOS->GetExtraBOFs(); // Let the initiator know that the listen has completed ConnLstnComplete(noErr); // ***FIXME: Is this the right amount of time. Errata had some mention of this StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); fState = kIrLAPSecReceiveState; StartDataReceive(); // jdg: wait until new state before issuing read break; case kIrDisconnectRequestEvent: XTRACE(kListenDisconnectEvent, 0, 0); XASSERT(fCurrentRequest != nil); HandleNDMDisconnectRequest(); break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: // REVIEW THIS ONE. Unsolicited connect, listen? case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleListenStateEvent: bad event"); break; } } // TIrLAP::HandleListenStateEvent //-------------------------------------------------------------------------------- // HandleReplyStateEvent (fState == kIrLAPReplyState) //-------------------------------------------------------------------------------- void TIrLAP::HandleReplyStateEvent(ULong event) { switch (event) { case kIrInputCompleteEvent: { XTRACE(kReplyRecdInputEvent, fRecdCmdRsp, 0); Boolean repostInput = true; if (RecdPollCmd(kIrLAPCmdXID)) { TXIDPacket xidCmd; // Parse the received XID command if (GotData(&xidCmd.fFormatId, kTXIDPacketSize - 2)) { if (((xidCmd.fFormatId == kIrLAPDiscoveryXIDFormat)) && ((xidCmd.fSrcDevAddr != 0) && (xidCmd.fSrcDevAddr != kIrLAPBroadcastDevAddr)) && ((xidCmd.fDstDevAddr == kIrLAPBroadcastDevAddr) || ((xidCmd.fDstDevAddr == fMyDevAddr) && (xidCmd.fFlags & kIrLAPDiscoverFlagsNewAddr)))) { if (xidCmd.fSlotNum != kIrLAPFinalSlot) { // Init some state first time through if (fDiscoverEnteredReplyState) { // Recd bcast xid or xid for me and not just the final - so enter reply state fState = kIrLAPReplyState; fDiscoverMaxSlots = IrSlotCounts[xidCmd.fFlags & kIrLAPDiscoverFlagsSlotMask]; fDiscoverSlot = random() % fDiscoverMaxSlots; fDiscoverReplied = false; fReplacementDevAddr = 0; #if forMac // FIXME - XID slots are too long (w/ write completion proc). I had to increase the time period // for it to work. StartTimer(fDiscoverMaxSlots * 140 * kMilliseconds, kIrQueryTimerExpiredEvent); #else StartTimer(fDiscoverMaxSlots * 100 * kMilliseconds, kIrQueryTimerExpiredEvent); #endif fDiscoverEnteredReplyState = false; } // Respond to the XID query if haven't yet and its my slot if (!fDiscoverReplied && (xidCmd.fSlotNum >= fDiscoverSlot)) { OutputXIDResponse(xidCmd); fDiscoverReplied = true; repostInput = false; } } else { // Final slot - all done with the XID game for now StopTimer(); if (fReplacementDevAddr) fMyDevAddr = fReplacementDevAddr; // Discovery reply complete fState = kIrLAPDisconnectedState; // ***MINIMUM: If this were a full implementation then the // listener (one who made a listen request) would receive some // sort of interim discovery indication. But for now only the // discoverer (one who made a discover request) is notified. //XASSERT(fCurrentRequest != nil); // JDG -- it *can* be nil when we're not doing much and get probed. if (fCurrentRequest && fCurrentRequest->fEvent == kIrDiscoverRequestEvent) { // Let the initiator know that the discovery phase has completed TIrDscInfo* discoveryInfo = TIrDscInfo::tIrDscInfo(); TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)fCurrentRequest; fCurrentRequest = nil; discoverReply->fEvent = kIrDiscoverReplyEvent; if (discoveryInfo == nil) { discoverReply->fResult = kIrDAErrNoMemory; } else { // Fill in, extract info for discover info discoveryInfo->SetVersion(xidCmd.fVersion); discoveryInfo->SetDeviceAddr(xidCmd.fSrcDevAddr); discoveryInfo->ExtractDevInfoFromBuffer(fInputBuffer); // Add it to the list of discovered devices discoverReply->fDiscoveredDevices->InsertLast(discoveryInfo); discoverReply->fResult = noErr; discoverReply->fPassiveDiscovery = true; } // In the case where we are replying to an XID cmd while // discovering and are now finished with the discovering // then we do not want to input again. repostInput = false; GetLMP->EnqueueEvent(discoverReply); } else { CIrDiscovery * discObj = GetDiscovery; if( discObj ) { TIrDscInfo* discoveryInfo = TIrDscInfo::tIrDscInfo(); TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)fCurrentRequest; // ? if (discoveryInfo ) { // Fill in, extract info for discover info discoveryInfo->SetVersion(xidCmd.fVersion); discoveryInfo->SetDeviceAddr(xidCmd.fSrcDevAddr); discoveryInfo->ExtractDevInfoFromBuffer(fInputBuffer); discObj->PassiveDiscovery( discoveryInfo ); } } } } } } } // If we sent an XID response (or perhaps we were responding because we were // discovered while discovering) then don't fall thru, wait for output to complete if (!repostInput) break; } // Fall thru to OutputComplete event to re-post input case kIrOutputCompleteEvent: XTRACE(kReplyOutputDoneEvent, fNextCmdRspToSend, fDiscoverSlot); StartInput(fIOBufferItem); break; case kIrQueryTimerExpiredEvent: XTRACE(kReplyTimeoutEvent, 0, 0); // ***FIXME: I got here because I missed the final slot query - if I responded // to the query for my slot then everything should be ok; however, if I did not // respond to a query for my slot then the other side doesn't know about me and // listen should probably terminate with some error. // ***I think that StopInput needs to be called here too. if (fReplacementDevAddr) fMyDevAddr = fReplacementDevAddr; fState = kIrLAPDisconnectedState; DebugLog("Missed final packet of peer discovery;g"); //StopInput(); // scc (?) was hanging here, let's reset it //StartTimer(5*1000, kIrMediaBusyTimerExpiredEvent); // 5 seconds abort read hack //StartInput(fIOBufferItem); // hang a read out // TODO - review this again break; case kIrDisconnectRequestEvent: XTRACE(kReplyDisconnectEvent, 0, 0); XASSERT(fCurrentRequest != nil); HandleNDMDisconnectRequest(); break; case kIrListenRequestEvent: // jdg hacking - just send the listen back to caller w/error { XTRACE(kReplyListenRequestEvent, 0, 0); TIrConnLstnReply* connLstnReply = (TIrConnLstnReply*)GetCurrentEvent(); connLstnReply->fEvent = kIrListenReplyEvent; connLstnReply->fResult = kIrDAErrToolBusy; GetLMP->EnqueueEvent(connLstnReply); } break; case kIrDiscoverRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleReplyStateEvent: bad event"); break; } } // TIrLAP:: HandleReplyStateEvent //-------------------------------------------------------------------------------- // HandleNDMDisconnectRequest //-------------------------------------------------------------------------------- void TIrLAP::HandleNDMDisconnectRequest() { // Stop any pending input, output and timers StopInput(); StopOutput(); StopTimer(); fDiscoverActive = false; // we are not discovering anymore // If a request is pending (discover, listen, connect) reply to it. if (fCurrentRequest != nil) { TIrEvent* reply = (TIrEvent*)fCurrentRequest; fCurrentRequest = nil; reply->fEvent = (UByte)RequestIdToReplyId(reply->fEvent); reply->fResult = kIrDAErrCancel; XTRACE(kNDMDisconnectFwdReply, fState, reply->fEvent); GetLMP->EnqueueEvent(reply); } // Reply to the disconnect request TIrDisconnectReply* discReply = (TIrDisconnectReply*)GetCurrentEvent(); discReply->fEvent = kIrDisconnectReplyEvent; discReply->fResult = kIrDAErrCancel; XTRACE(kNDMDisconnectReply, fState, 0); // Change state to disconnected fState = kIrLAPDisconnectedState; GetLMP->EnqueueEvent(discReply); } // HandleNDMDisconnectRequest //-------------------------------------------------------------------------------- // HandlePriReceiveStateEvent (fState == kIrLAPPriReceiveState) //-------------------------------------------------------------------------------- void TIrLAP::HandlePriReceiveStateEvent(ULong event) { switch (event) { case kIrFinalTimerExpiredEvent: GetIrDevice->Stats_ReceiveTimeout(); XTRACE(kPriReceiveFinalTimeoutEvent, 0, 0); //DebugLog(" precv final timer"); StopInput(); if (fRetryCount >= fDisconnectLinkLimit) { // Apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrTimeout); } else { if (fRetryCount == fDisconnectWarningLimit) { fInBrokenBeam = true; //gConnectionBroken = true; } // if( fRetryCount == 0 ) { // Remember time of first retry // fInitialRetryTime = TickCount(); // and smack the hardware // } fRetryCount++; // Output RR/RNR depending on whether local is busy or not OutputControlFrame(fLocalBusy ? kIrLAPFrameRNR : kIrLAPFrameRR); } break; case kIrTurnaroundTimerExpiredEvent: // Check for pending disconnect if (fEnteringCloseState) { fNextCmdRspToSend = kIrLAPCmdDISC; fState = kIrLAPPriCloseState; } // Check for set localBusy pending and set it and notify peer device else if (fSetLocalBusyPending) { XTRACE(kSetLocalBusyEvent, 0, 0); fNextCmdRspToSend = kIrLAPFrameRNR; fLocalBusy = true; fSetLocalBusyPending = false; } // Check for clr localBusy pending and clear it and notify peer device else if (fClrLocalBusyPending) { XTRACE(kClrLocalBusyEvent, 0, 0); fNextCmdRspToSend = kIrLAPFrameRR; fLocalBusy = false; fClrLocalBusyPending = false; } // Can we begin transmitting now? else if (fNextCmdRspToSend == kIrLAPFrameINFO) { // NOTE: This is different from the secondary and also different // from the IrLAP spec. The secondary will only enter XMIT if both // remoteBusy is false and there are pending requests. The IrLAP spec // allows primary to enter XMIT no matter what. But, this doesn't make // sense if the secondary is busy (i.e. remoteBusy is true), because // then there will be an entire P-timer timeout before the primary // receives again to find out if the secondary is no longer busy. So, // in this case I only enter XMIT if fRemoteBusy is false. // if (fRemoteBusy) { // fNextCmdRspToSend = fLocalBusy ? kIrLAPFrameRNR : kIrLAPFrameRR; // } // else { //CPD moved into Pri // StartTimer(fPollTimerTimeout, kIrPollTimerExpiredEvent); // XMIT state fValidRecdNr = 0; fState = kIrLAPPriTransmitState; NextState(kIrTurnaroundTimerExpiredEvent); break; // } } OutputControlFrame(fNextCmdRspToSend); // Note: OutputControlFrame sets poll bit break; case kIrOutputCompleteEvent: XTRACE(kPriReceiveOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { case kIrLAPFrameRR: case kIrLAPFrameRNR: // Finish up after sending RR/RNR StartDataReceive(); StartTimer(fFinalTimerTimeout, kIrFinalTimerExpiredEvent); break; default: DebugLog("TIrLAP::HandlePriReceiveStateEvent: bad output state"); break; } break; case kIrInputCompleteEvent: XTRACE(kPriReceiveRecdInputEvent, fRecdCmdRsp, (fRecdNs << 8) | fRecdNr); // Stop the F-timer if received frame with Final bit set if (RecdFinal()) { StopTimer(); } // We received some input so reset the retry count that is used for the // disconnect warning or disconnect link actions in final timer timeout. fRetryCount = 0; #if forMac fInitialRetryTime = 0; fInBrokenBeam = false; //gConnectionBroken = false; #endif // Init most common cases for responses fNextCmdRspToSend = kIrLAPFrameINFO; // TESTING if (!RecdFinalRsp(kIrLAPRspFRMR)) // if anything other than FRMR gLastWasFRMR = false; // then reset the flag // Handle received data frames and supervisor commands if (!RecdUFrame() || RecdRsp(kIrLAPRspUI)) { if (RecdInvalidNrOrNs()) { // Record this as a protocol error fProtocolErrs++; // Disconnect if received invalid Nr or Ns // NOTE: The upper layers do not support reset so just disconnect now if (0) { // jdg. hit with hammer fEnteringCloseState = true; //DebugLog("fEnteringCloseState setting true #1"); // jdg } XTRACE(kIgnoringInvalidNrNs, 0, 0); // jdg /**if (1) { // TEMP TEMP TEMP TEMP extern struct IrDALogHdr gIrDALog; gIrDALog.fTracingOn = false; // HACK turn off tracing so we can dump w/tool DebugLog("lap: invalid ns/nr. logging stopped"); // temp fEnteringCloseState = true; // TEMP TEMP TEMP TEMP }***/ } else { ProcessRecdInfoOrSuperFrame(); } } // else if (RecdFinalRsp(kIrLAPRspFRMR)) { UInt8 frmrInfo[3]; fInputBuffer->Getn( ( UInt8 * )&frmrInfo, 3 ); // format XA, BC XTRACE(kPriReceiveFRMREvent, frmrInfo[0], *( UInt16 *)&frmrInfo[1] ); /*******/ GetIrDevice->Stats_TransmitTimeout(); /**** TEMP *** LOG FRMR RCVD *****/ // Close down the link // JDG: but only if we've gotten more than one of these in a row!!! if (gLastWasFRMR) { fEnteringCloseState = true; // DebugLog("fEnteringCloseState setting true #2"); XTRACE(kLogEnteringCloseState, 2, 2); } else gLastWasFRMR = true; // if get another one, shut down the link } // else if (RecdFinalRsp(kIrLAPRspRD) || RecdFinalRsp(kIrLAPRspRNRM)) { // Close down the link fEnteringCloseState = true; XTRACE(kLogEnteringCloseState, 3, 3); //DebugLog("fEnteringCloseState setting true #3"); } // Received some other response w/final bit on - go to xmit state else if (RecdFinal()) { // Record this as a protocol error fProtocolErrs++; // Handled below } // Received some other response w/final bit off - continue receiving else { // Record this as a protocol error fProtocolErrs++; // Handled below } // If didn't disconnect for some reason above, receive again or xmit after delay if (fState == kIrLAPPriReceiveState) { if (RecdFinal()) { // If received final, transmitting next (after min turnaround delay) if( fRecdCmdRsp == kIrLAPFrameRNR ) { StartTimer(fPollTimerTimeout>>2, kIrTurnaroundTimerExpiredEvent); } else StartTimer(fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); // StartMinTurnAroundTimer( fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); } else { // If didn't receive final, receive again StartDataReceive(); } } break; case kIrPutDataRequestEvent: XTRACE(kPriReceivePutDataEvent, 0, 0); PostponePutRequest(); break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrDisconnectRequestEvent: XTRACE(kPriReceiveDisconnectEvent, 0, 0); XASSERT(fPendingDisconnect == nil); fPendingDisconnect = GetCurrentEvent(); fEnteringCloseState = true; XTRACE(kLogEnteringCloseState, 4, 4); //DebugLog("fEnteringCloseState setting true #4"); break; case kIrLocalBusyClearedEvent: // Don't need to do anything special here - handled above XTRACE(kPriReceiveClrLocBsyPendEvent, 0, 0); // XASSERT(fClrLocalBusyPending); -- now bogus ... jdg break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandlePriReceiveStateEvent: bad event"); break; } } // TIrLAP::HandlePriReceiveStateEvent //-------------------------------------------------------------------------------- // HandlePriTransmitStateEvent (fState == kIrLAPPriTransmitState) //-------------------------------------------------------------------------------- void TIrLAP::HandlePriTransmitStateEvent(ULong event) { switch (event) { case kIrPollTimerExpiredEvent: #if forMac //fIrSIR->TransmitTimeout(); // jdg - bogus, normal turnaround, not timeout #endif XTRACE(kPriTransmitPollTimeoutEvent, 0, 0); fWaitingForPollTimer = false; StopInput(); OutputControlFrame(kIrLAPFrameRR); // Note: OutputControlFrame sets poll bit break; case kIrTurnaroundTimerExpiredEvent: XTRACE(kPriTransmitEnterEvent, 0, 0); if (!fPendingPutRequests->IsEmpty() && ! fRemoteBusy) { // There is something waiting to be sent. TIrPutRequest* putRequest = (TIrPutRequest*)fPendingPutRequests->Last(); XASSERT(putRequest != nil); fPendingPutRequests->RemoveLast(); // Check if there are more put pending. If not, then I can set p/f and turn the link fPutReqsPending = !fPendingPutRequests->IsEmpty(); // Only start the poll timer if there are multiple frames to send if( ( fWindow > 1 ) && fPutReqsPending ) { StartTimer(fPollTimerTimeout, kIrPollTimerExpiredEvent); } OutputDataFrame(putRequest, !fLocalBusy && ((fWindow == 1) || !fPutReqsPending)); } else { /* // We have nothing to send and the remote is still busy or we are trading RR's. // Delay a bit to let the remote process its buffers (1/4 turn around time). If // something else turned the link then just turn it back with a RR. if( fRemoteBusy || ( fLastCmdRsp == kIrLAPFrameRR && fRecdCmdRsp == kIrLAPFrameRR ) ) { fWaitingForPollTimer = true; StartTimer( fPollTimerTimeout >> 2, kIrPollTimerExpiredEvent ); } else { */ NextState( kIrPollTimerExpiredEvent ); // so exit state with a RR /* // I received a full window of data last time, so chances are there is more // to come. Release the link immediately. if( fRecdCmdRsp == kIrLAPFrameINFO ) NextState( kIrPollTimerExpiredEvent ); // so exit state with a RR else // This gives the client time to process any data received, instead of // spending all the time trading RRs back and forth. StartTimer( fPollTimerTimeout >> 2, kIrPollTimerExpiredEvent ); } */ } break; case kIrOutputCompleteEvent: XTRACE(kPriTransmitOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { // this is really the last cmd sent! case kIrLAPFrameINFO: if (--fWindow > 0 && fPutReqsPending) { // Continue sending requests (if any and until P-timer expires) if (!fPendingPutRequests->IsEmpty()) { TIrPutRequest* putRequest = (TIrPutRequest*)fPendingPutRequests->Last(); XASSERT(putRequest != nil); fPendingPutRequests->RemoveLast(); // Take note of whether additional put requests are pending now fPutReqsPending = !fPendingPutRequests->IsEmpty(); // Stop the timer if this is the last frame to send if ( ( fWindow == 1 ) || !fPutReqsPending ) { StopTimer(); } OutputDataFrame(putRequest, !fLocalBusy && ((fWindow == 1) || !fPutReqsPending)); } break; } else { // Reset window fWindow = fPeerWindowSize; if (fLocalBusy) { // Send RNR if local busy OutputControlFrame(kIrLAPFrameRNR); // Note: OutputControlFrame sets poll bit break; } else { // fall thru and switch to RECV } } case kIrLAPFrameRR: case kIrLAPFrameRNR: //StartDataReceive(); // Post the read as soon as possible StartTimer(fFinalTimerTimeout, kIrFinalTimerExpiredEvent); fState = kIrLAPPriReceiveState; StartDataReceive(); // jdg: wait until state change to do read break; default: DebugLog("TIrLAP::HandlePriTransmitStateEvent: bad output state"); break; } break; case kIrPutDataRequestEvent: XTRACE(kPriTransmitPutDataEvent, 0, 0); // Only one choice for primary (now): // If output in progress, queue the request. // If other requests in queue, queue the request. // And if no requests pending we already gave control back to primary, queue the request. // If the p/f isn't set (fPutReqsPending), it may make it out this time around if (fWaitingForPollTimer && !fRemoteBusy) { // performance hack fWaitingForPollTimer = false; // don't send out an RR StopTimer(); OutputDataFrame((TIrPutRequest*)GetCurrentEvent(), true); // always set final bit } else PostponePutRequest(); /* // Queue up the request if outputting now or other requests in queue (to maintain order) if (fOutputInProgress || !fPendingPutRequests->IsEmpty()) { XTRACE(kPriTransmitPutDataEvent, 0, 0); PostponePutRequest(); } // No requests currently active or pending. Send this one out. else { XTRACE(kPriTransmitPutDataEvent, 1, 0); if (fWindow == 1) { StopTimer(); } OutputDataFrame((TIrPutRequest*)GetCurrentEvent(), !fLocalBusy && (fWindow == 1)); } */ break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrDisconnectRequestEvent: XTRACE(kPriTransmitDisconnectEvent, 0, 0); XASSERT(fPendingDisconnect == nil); fPendingDisconnect = GetCurrentEvent(); fEnteringCloseState = true; XTRACE(kLogEnteringCloseState, 5, 5); //DebugLog("fEnteringCloseState setting true #5"); break; case kIrLocalBusyClearedEvent: // Don't need to do anything special here - handled in secondary receive // ***FIXME: Could fLocalBusy be cleared here sometimes? Would it improve performance any? XTRACE(kPriReceiveClrLocBsyPendEvent, 0, 0); XASSERT(fClrLocalBusyPending); break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); //DebugLog("TIrLAP::HandlePriTransmitStateEvent: bad event"); // fIrDA->ReleaseEventBlock(GetCurrentEvent()); // jdg: free the event break; } } // TIrLAP::HandlePriTransmitStateEvent //-------------------------------------------------------------------------------- // HandlePriCloseStateEvent (fState == kIrLAPPriCloseState) //-------------------------------------------------------------------------------- void TIrLAP::HandlePriCloseStateEvent(ULong event) { switch (event) { case kIrFinalTimerExpiredEvent: XTRACE(kPriCloseFinalTimeoutEvent, 0, 0); StopInput(); if (++fRetryCount >= kMaxDisconnectRetries) { // Apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrTimeout); } else { // Resend the disconnect OutputControlFrame(kIrLAPCmdDISC); // Note: OutputControlFrame sets poll bit } break; case kIrOutputCompleteEvent: XTRACE(kPriCloseOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { case kIrLAPCmdDISC: if (fEnteringCloseState) { fEnteringCloseState = false; fRetryCount = 0; } StartTimer(fFinalTimerTimeout, kIrFinalTimerExpiredEvent); StartInput(fIOBufferItem); break; default: DebugLog("TIrLAP::HandlePriCloseStateEvent: bad output state"); break; } break; case kIrInputCompleteEvent: XTRACE(kPriCloseRecdInputEvent, fRecdCmdRsp, (fRecdNs << 8) | fRecdNr); if (RecdFinalRsp(kIrLAPRspUA) || RecdFinalRsp(kIrLAPRspDM)) { // Stop timer, apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrCancel); } else { // Continue waiting for the UA/DM response StartInput(fIOBufferItem); } break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrPutDataRequestEvent: NotConnectedCompletion(); // jdg: Yes, send to CIrLSAP, lmp doesn't handle this //RejectRequest(GetCurrentEvent(), kCommErrNotConnected); // jdg: send to lmp, not directly to lsapconn break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; case kIrDisconnectRequestEvent: // jdg: hold this until close is done XTRACE(kPriCloseDisconnectRequest, 0, 0); fPendingRequests->InsertLast(GetCurrentEvent()); break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandlePriCloseStateEvent: bad event"); break; } } // TIrLAP::HandlePriCloseStateEvent //-------------------------------------------------------------------------------- // HandleSecReceiveStateEvent (fState == kIrLAPSecReceiveState) //-------------------------------------------------------------------------------- void TIrLAP::HandleSecReceiveStateEvent(ULong event) { switch (event) { case kIrWatchdogTimerExpiredEvent: GetIrDevice->Stats_ReceiveTimeout(); XTRACE(kSecReceiveWDTimeoutEvent, 0, 0); if (fRetryCount >= fDisconnectLinkLimit) { // Apply default conn parms, send disconnect indication, state = NDM StopInput(); DisconnectComplete(kIrDAErrTimeout); } else { if (fRetryCount == fDisconnectWarningLimit) { fInBrokenBeam = true; //gConnectionBroken = true; //if( gIrDAPrefs.DoBrokenBeamWarning() ) // BrokenBeamNotification( this ); } //if( fRetryCount == 0 ) { // Remember time of first retry // fInitialRetryTime = TickCount(); // and smack the hardware // fNextKickScc = fInitialRetryTime + 60 * 4; // in 4 seconds if still deaf //} fRetryCount++; StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); } break; case kIrTurnaroundTimerExpiredEvent: // Check for pending disconnect if (fEnteringCloseState) { fNextCmdRspToSend = fRespondingToDisconnect ? kIrLAPRspUA : kIrLAPRspRD; fState = kIrLAPSecCloseState; } // Check for pending FRMR response else if (fFRMRPending) { OutputFRMRResponse(); fFRMRPending = false; break; } // Check for set localBusy pending and set it and notify peer device else if (fSetLocalBusyPending) { XTRACE(kSetLocalBusyEvent, 0, 0); fNextCmdRspToSend = kIrLAPFrameRNR; fLocalBusy = true; fSetLocalBusyPending = false; } // Check for clr localBusy pending and clear it and notify peer device else if (fClrLocalBusyPending) { XTRACE(kClrLocalBusyEvent, 0, 0); fNextCmdRspToSend = kIrLAPFrameRR; fLocalBusy = false; fClrLocalBusyPending = false; } // Can we begin transmitting now? else if (fNextCmdRspToSend == kIrLAPFrameINFO) { if (fRemoteBusy || fPendingPutRequests->IsEmpty()) { fNextCmdRspToSend = fLocalBusy ? kIrLAPFrameRNR : kIrLAPFrameRR; } else { fValidRecdNr = 0; fState = kIrLAPSecTransmitState; NextState(kIrTurnaroundTimerExpiredEvent); break; } } OutputControlFrame(fNextCmdRspToSend); // Note: OutputControlFrame sets final bit break; case kIrOutputCompleteEvent: XTRACE(kSecReceiveOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { case kIrLAPFrameRR: case kIrLAPFrameRNR: case kIrLAPRspFRMR: // Finish up after sending RR/RNR/FRMR StartDataReceive(); StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); break; default: DebugLog("TIrLAP::HandleSecReceiveStateEvent: bad output state"); break; } break; case kIrInputCompleteEvent: XTRACE(kSecReceiveRecdInputEvent, fRecdCmdRsp, (fRecdNs << 8) | fRecdNr); // Stop the Watchdog-timer if received frame with Poll bit set if (RecdPoll()) { StopTimer(); } // We received some input so reset the retry count that is used for the // disconnect warning or disconnect link actions in watchdog timeout. fRetryCount = 0; #if forMac fInitialRetryTime = 0; //fNextKickScc = 0; fInBrokenBeam = false; //gConnectionBroken = false; #endif // Init most common cases for responses fNextCmdRspToSend = kIrLAPFrameINFO; // Ignore any non-poll bit set frames if in "error state" (waiting to send FRMR) if (fFRMRPending && !RecdPoll()) { // Just ignore it } // Handle received data frames and supervisor commands else if (!RecdUFrame() || RecdCmd(kIrLAPRspUI)) { if (RecdInvalidNrOrNs()) { // Record this as a protocol error fProtocolErrs++; // Prepare FRMR response - send it when we receive poll bit PrepareFRMRResponse(); } else { ProcessRecdInfoOrSuperFrame(); } } // else if (RecdPollCmd(kIrLAPCmdDISC)) { // Reply to the disconnect fEnteringCloseState = true; XTRACE(kLogEnteringCloseState, 6, 6); //DebugLog("fEnteringCloseState setting true #6"); fRespondingToDisconnect = true; } // else if (RecdPollCmd(kIrLAPCmdSNRM)) { if (fRecdAddr == fConnAddr) { // Reject the SNRM (reset not supported by IrLAPConn) fEnteringCloseState = true; XTRACE(kLogEnteringCloseState, 7, 7); //DebugLog("fEnteringCloseState setting true #7"); fRespondingToDisconnect = false; } else { // Ignore connection SNRMs from other sources // Kind of a hack forcing poll bit off so StartDataReceive is called below fRecdPF = 0; } } // else if (RecdFinalRsp(kIrLAPRspDM)) { // Apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrCancel); } // Received some other unrecognized command - send FRMR else { // Record this as a protocol error fProtocolErrs++; // Send FRMR PrepareFRMRResponse(); } // If didn't disconnect for some reason above, receive again or xmit after delay if (fState == kIrLAPSecReceiveState) { if (RecdPoll()) { // If received poll, transmitting next (after min turnaround delay) StartTimer(fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); // StartMinTurnAroundTimer( fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); } else { // If didn't receive poll, receive again StartDataReceive(); } } break; case kIrPutDataRequestEvent: XTRACE(kSecReceivePutDataEvent, 0, 0); PostponePutRequest(); break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrDisconnectRequestEvent: XTRACE(kSecReceiveDisconnectEvent, 0, 0); XASSERT(fPendingDisconnect == nil); fPendingDisconnect = GetCurrentEvent(); fEnteringCloseState = true; //DebugLog("fEnteringCloseState setting true #8"); XTRACE(kLogEnteringCloseState, 8, 8); fRespondingToDisconnect = false; break; case kIrLocalBusyClearedEvent: // Don't need to do anything special here - handled above XTRACE(kSecReceiveClrLocBsyPendEvent, 0, 0); XASSERT(fClrLocalBusyPending); break; case kIrConnectRequestEvent: // jdg added case kIrListenRequestEvent: // jdg added XTRACE(kSecReceiveConnLstnRequest, 0, 0); check(fCurrentRequest == nil); fCurrentRequest = GetCurrentEvent(); // hack so we can use ConnLstnComplete ConnLstnComplete(noErr); break; case kIrDiscoverRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleSecReceiveStateEvent: bad event"); break; } } // TIrLAP::HandleSecReceiveStateEvent //-------------------------------------------------------------------------------- // HandleSecTransmitStateEvent (fState == kIrLAPSecTransmitState) //-------------------------------------------------------------------------------- void TIrLAP::HandleSecTransmitStateEvent(ULong event) { switch (event) { case kIrTurnaroundTimerExpiredEvent: { XTRACE(kSecTransmitEnterEvent, 0, 0); // We just entered the transmit state // Should only be here if there are pending put requests and remote is not busy XASSERT(!fPendingPutRequests->IsEmpty() && !fRemoteBusy); TIrPutRequest* putRequest = (TIrPutRequest*)fPendingPutRequests->Last(); XASSERT(putRequest != nil); fPendingPutRequests->RemoveLast(); // Take note of whether additional put requests are pending now, because there // may not be any now, but a request might come in while the data is being // output, but we're locked in now and need to finish what we started after the // output completes (in other words, if we set poll bit here because no more // reqs pending (vs window == 1), then we have to give control to secondary after // the output whether another request comes in or not). fPutReqsPending = !fPendingPutRequests->IsEmpty(); OutputDataFrame(putRequest, !fLocalBusy && ((fWindow == 1) || !fPutReqsPending)); } break; case kIrOutputCompleteEvent: XTRACE(kSecTransmitOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { case kIrLAPFrameINFO: if ((--fWindow > 0) && fPutReqsPending) { // Continue sending requests (if pending and peer device has room) TIrPutRequest* putRequest = (TIrPutRequest*)fPendingPutRequests->Last(); XASSERT(putRequest != nil); fPendingPutRequests->RemoveLast(); // Take note of whether additional put requests are pending now (see note above) fPutReqsPending = !fPendingPutRequests->IsEmpty(); OutputDataFrame(putRequest, !fLocalBusy && ((fWindow == 1) || !fPutReqsPending)); break; } else { // Reset window fWindow = fPeerWindowSize; if (fLocalBusy) { // Send RNR if local busy OutputControlFrame(kIrLAPFrameRNR); // Note: OutputControlFrame sets final bit break; } else { // fall thru and switch to RECV } } case kIrLAPFrameRR: case kIrLAPFrameRNR: StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); fState = kIrLAPSecReceiveState; StartDataReceive(); // jdg: change state before issuing read - can come right back // with some data ... before I get a chance to changestate! break; default: DebugLog("TIrLAP::HandleSecTransmitStateEvent: bad output state"); break; } break; case kIrPutDataRequestEvent: XTRACE(kSecTransmitPutDataEvent, 0, 0); // Only one choice for secondary: // If output in progress, queue the request. // If other requests in queue, queue the request. // And if no requests pending we already gave control back to primary, queue the request. PostponePutRequest(); break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrDisconnectRequestEvent: XTRACE(kSecTransmitDisconnectEvent, 0, 0); XASSERT(fPendingDisconnect == nil); fPendingDisconnect = GetCurrentEvent(); fEnteringCloseState = true; //DebugLog("fEnteringCloseState setting true #9"); XTRACE(kLogEnteringCloseState, 9, 9); break; case kIrLocalBusyClearedEvent: // Don't need to do anything special here - handled in secondary receive // ***FIXME: Could fLocalBusy be cleared here sometimes? Would it improve performance any? XTRACE(kSecTransmitClrLocBsyPendEvent, 0, 0); XASSERT(fClrLocalBusyPending); break; case kIrConnectRequestEvent: // jdg added case kIrListenRequestEvent: // jdg added XTRACE(kSecTransmitConnLstnRequest, 0, 0); check(fCurrentRequest == nil); fCurrentRequest = GetCurrentEvent(); // hack so we can use ConnLstnComplete ConnLstnComplete(noErr); break; case kIrDiscoverRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleSecTransmitStateEvent: bad event"); break; } } // TIrLAP::HandleSecTransmitStateEvent //-------------------------------------------------------------------------------- // HandleSecCloseStateEvent (fState == kIrLAPSecCloseState) //-------------------------------------------------------------------------------- void TIrLAP::HandleSecCloseStateEvent(ULong event) { switch (event) { case kIrWatchdogTimerExpiredEvent: XTRACE(kSecCloseWDTimeoutEvent, 0, 0); StopInput(); // Apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrTimeout); break; case kIrTurnaroundTimerExpiredEvent: OutputControlFrame(fNextCmdRspToSend); // Note: OutputControlFrame sets final bit break; case kIrOutputCompleteEvent: XTRACE(kSecCloseOutputDoneEvent, fNextCmdRspToSend, 0); switch (fNextCmdRspToSend) { case kIrLAPRspRD: fEnteringCloseState = false; StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); StartInput(fIOBufferItem); break; case kIrLAPRspUA: // Apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrCancel); break; default: DebugLog("TIrLAP::HandleSecCloseStateEvent: bad output state"); break; } break; case kIrInputCompleteEvent: XTRACE(kSecCloseRecdInputEvent, fRecdCmdRsp, 0); // Stop the watchdog timer StopTimer(); if (RecdPollCmd(kIrLAPCmdDISC)) { // Acknowledge the disconnect (after min turnaround delay) fNextCmdRspToSend = kIrLAPRspUA; StartTimer(fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); // StartMinTurnAroundTimer( fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); } else if (RecdFinalRsp(kIrLAPRspDM)) { // Stop timer, apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrCancel); } else if (RecdPoll()) { // Resend the RD (after min turnaround delay) fNextCmdRspToSend = kIrLAPRspRD; StartTimer(fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); // StartMinTurnAroundTimer( fMinTurnAroundTimeout, kIrTurnaroundTimerExpiredEvent); } else { // Continue waiting for the DISC/DM response StartTimer(fWatchdogTimeout, kIrWatchdogTimerExpiredEvent); StartInput(fIOBufferItem); } break; case kIrCancelPutRequestEvent: CancelPutRequest(); break; case kIrPutDataRequestEvent: NotConnectedCompletion(); // jdg - yes, pls do send back to CIrLSAP, not to lmp which drops it //RejectRequest(GetCurrentEvent(), kCommErrNotConnected); // jdg: send to lmp, not directly to lsapconn break; case kIrDiscoverRequestEvent: case kIrListenRequestEvent: case kIrConnectRequestEvent: XTRACE(kUnexpectedEvent, fState, event); RejectRequest(GetCurrentEvent(), kIrDAErrWrongState); // don't do it, don't crash break; case kIrDisconnectRequestEvent: // jdg: hold this until close is done XTRACE(kSecCloseDisconnectRequest, 0, 0); fPendingRequests->InsertLast(GetCurrentEvent()); break; default: XTRACE(kUnexpectedEvent, fState, event); DebugLog("TIrLAP::HandleSecCloseStateEvent: bad event"); break; } } // TIrLAP::HandleSecCloseStateEvent //-------------------------------------------------------------------------------- // UpdateNrReceived //-------------------------------------------------------------------------------- void TIrLAP::UpdateNrReceived() { XTRACE(kUpdateNrReceived, fNextToAck, fRecdNr); while (fNextToAck != fRecdNr) { TIrPutReply* putReply = (TIrPutReply*)fPutRequests[fNextToAck]; fPutRequests[fNextToAck] = nil; // Send back reply (or in some cases free up the request block) PutComplete(putReply, noErr); // Clear the fValidRecdNr bit that corresponds to the "ack'd" fValidRecdNr &= ~(1 << fNextToAck); // Check next to ack (taking wrapping into account) fNextToAck = (fNextToAck + 1) & 0x7; } } // TIrLAP::UpdateNrReceived //-------------------------------------------------------------------------------- // ResendRejectedFrames //-------------------------------------------------------------------------------- void TIrLAP::ResendRejectedFrames() { // ***FIXME: Should the fValidRecdNr bits be "reduced" by the amount resent // And if not, should the fPutRequests be nil'd out or left alone? ULong index = (fVs - 1) & 0x7; XTRACE(kResendRejectedFrames, 0, index); // debug // Go thru the list backwards so frames are resent in the correct order while (true) { TIrPutRequest* putRequest = fPutRequests[index]; XASSERT(putRequest != nil); if (putRequest == nil) { // Client must have cancelled put and peer device is asking for resend // Just get out of here, because things will be shut down soon anyway. break; } XTRACE(kResendRejectedFrames, 1, index); // jdg fPendingPutRequests->InsertLast(putRequest); fPutRequests[index] = nil; fDataRetries++; #if forMac GetIrDevice->Stats_PacketResent(); // testing TEMP TEMP TEMP TEMP TEMP //if (fIrSIR->fIRStats.resent == 100) { // if hit 100 resends // extern struct IrDALogHdr gIrDALog; // gIrDALog.fTracingOn = false; // HACK turn off tracing so we can dump w/tool // DebugLog("Ok, 100 resends. logging turned off, dump the log"); //} #endif if (index == fRecdNr) { break; } else { index = (index - 1) & 0x7; } } // Back up Vs, ready for the first resend fVs = fRecdNr; XTRACE(kResendRejectedFrames, 2, fVs); // jdg } // TIrLAP::ResendRejectedFrames //-------------------------------------------------------------------------------- // ProcessRecdInfoOrSuperFrame //-------------------------------------------------------------------------------- void TIrLAP::ProcessRecdInfoOrSuperFrame() { // This is a common routine used by both primary and secondary receive states // It should only be called for information/supervisory frames or the UI frame. // It is assumed that the invalid Ns/Nr case has already been checked for. // Also if the F/P bit was received the caller should have already stopped the timer. // And on entry, fNextCmdRspToSend is defaulted (preset) to kIrLAPFrameINFO. XTRACE(kProcessISFrame, RecdCmd(), RecdRsp()); if (!Recd(kIrLAPRspUI)) { // Primary should only receive responses, secondary should only receive commands if ((fPrimary && RecdCmd()) || (!fPrimary && RecdRsp())) { // Record this as a protocol error fProtocolErrs++; // Stop timer, apply default conn parms, send disconnect indication, state = NDM DisconnectComplete(kIrDAErrPacket); return; } // Update Nr received - i.e. reply to put requests, remove buffers from list UpdateNrReceived(); } switch(fRecdCmdRsp) { case kIrLAPFrameINFO: // Process received data if not fLocalBusy (or local busy pending) if (!fLocalBusy && !fSetLocalBusyPending) { if (RecdUnexpectedNs()) { // Not used if not final/poll frame but doesn't hurt to set it fNextCmdRspToSend = kIrLAPFrameRR; #if forMac GetIrDevice->Stats_PacketDropped(); #endif } else { // This should never happen! XASSERT(fInputBuffer != fIOBufferItem); if (fInputBuffer == fIOBufferItem) { // Debugging DebugLog("oops, fInputbuffer == fIObufferItem 0x%lx", (UInt32) fInputBuffer); //IrDALogTracingOff(); // Debugging! } // Let the demultipexor know that a buffer has arrived GetLMP->Demultiplexor(fInputBuffer); // Clear the fValidRecdNs bit that corresponds to current fVr fValidRecdNs &= ~(1 << fVr); // Update fValidRecdNs to include next maximum allowed received Ns fValidRecdNs |= 1 << ((fVr + fMyWindowSize) & 0x7); // Record that I received/accepted a frame fVr = (fVr + 1) & 0x7; // Next receive requires new buffer //fNeedNewInputBuffer = true; fInputBuffer = nil; // If we just passed a buffer to the demultiplexor and there was not // a "get" request pending to consume the data immediately and there // are no more buffers then we are entering a local busy condition. if (fGetBufferAvail == 0) { XTRACE(kSetLocalBusyPendingEvent, 0, 0); fSetLocalBusyPending = true; } if (RecdUnexpectedNr()) { ResendRejectedFrames(); } } } #if forMac else GetIrDevice->Stats_PacketDropped(); // Packet was dropped due to flow control #endif break; case kIrLAPFrameRR: fRemoteBusy = false; if (RecdUnexpectedNr() && !fLocalBusy) { ResendRejectedFrames(); } #if forMac GetIrDevice->Stats_RRRec(); #endif break; case kIrLAPFrameREJ: ResendRejectedFrames(); #if forMac GetIrDevice->Stats_REJRec(); #endif break; case kIrLAPFrameSREJ: ResendRejectedFrames(); #if forMac GetIrDevice->Stats_SREJRec(); #endif break; case kIrLAPFrameRNR: fRemoteBusy = true; fNextCmdRspToSend = fLocalBusy ? kIrLAPFrameRNR : kIrLAPFrameRR; #if forMac GetIrDevice->Stats_RNRRec(); // Remote is in flow control #endif break; default: XASSERT(fRecdCmdRsp == kIrLAPRspUI); if (!fLocalBusy) { // ***FUTURE: Handle ui here // Next receive requires new buffer // ***FUTURE: Put back next line when/if actual ui receiver implemented // fNeedNewInputBuffer = true; } break; } } // TIrLAP::ProcessRecdInfoOrSuperFrame //-------------------------------------------------------------------------------- // CopyStatsTo //-------------------------------------------------------------------------------- void TIrLAP::CopyStatsTo(TCMOSlowIRStats* irStats) { #if forMac #pragma unused(irStats) #else // Just copy the stats that IrLAP modifies irStats->dataRetries = fDataRetries; irStats->protocolErrs = fProtocolErrs; #endif } // TIrLAP::CopyStatsTo //-------------------------------------------------------------------------------- // ResetStats //-------------------------------------------------------------------------------- void TIrLAP::ResetStats() { // Just clear the fields that IrLAP modifies fDataRetries = 0; fProtocolErrs = 0; } // TIrLAP::ResetStats //=============================== Utility methods ================================ //-------------------------------------------------------------------------------- // ParseNegotiateAndInitConnState //-------------------------------------------------------------------------------- IrDAErr TIrLAP::ParseNegotiateAndInitConnState(Boolean primary) { // **FIXME: Should this be broken up a bit to make patching easier? IrDAErr result; ULong numBuffers; ULong bufferSize; CBufferSegment* bufferItem; TTimeout retryTimeUnit; TTimeout disconnectLinkTime; fPrimary = primary; // Parse negotiation parms passed (may not be any, don't panic - use defaults) result = fPeerQOS->ExtractInfoFromBuffer(fInputBuffer); XREQUIRENOT(result, Fail_QOS_ExtractInfo); // Negotiate conn parms result = fMyQOS->NegotiateWith(fPeerQOS); XREQUIRENOT(result, Fail_QOS_NegotiateMe); result = fPeerQOS->NegotiateWith(fMyQOS); XREQUIRENOT(result, Fail_QOS_NegotiatePeer); // Apply conn parms (that can be) ahead of time // Note: DON'T change baud rate yet XASSERT(fMyQOS->GetBaudRate() == fPeerQOS->GetBaudRate()); XTRACE(kQOSSetBaudRateEvent, 0, fMyQOS->GetBaudRate()); // Set various timeout values (Note: WD timeout is 1.25x time primary can hold link) fPollTimerTimeout = fMyQOS->GetMaxTurnAroundTime(); fFinalTimerTimeout = fPeerQOS->GetMaxTurnAroundTime(); // HACK -- since we don't really wait for our transmit queue to empty before // turning the link around, we have to bump up the time we wait for our peer's // response by the time we could be spending transmitting. // TEST == incr finaltimer (and polltimer) by our max time //fPollTimerTimeout += fMyQOS->GetMaxTurnAroundTime(); //fFinalTimerTimeout += fMyQOS->GetMaxTurnAroundTime(); // TEMP TEMP. TODO: impl real output complete so the timer is correct? //fPollTimerTimeout = fPeerQOS->GetMaxTurnAroundTime(); //fFinalTimerTimeout = fMyQOS->GetMaxTurnAroundTime(); //fFinalTimerTimeout += fPollTimerTimeout; // JDG HACKING fWatchdogTimeout = fPeerQOS->GetMaxTurnAroundTime() + (fPeerQOS->GetMaxTurnAroundTime() >> 2); fMinTurnAroundTimeout = fPeerQOS->GetMinTurnAroundTime(); //DebugLog(" test - setting peer min ttime to 10 ms;g"); //fMinTurnAroundTimeout = 10; // TEST TEST TEST new hardware // Set link disconnect warning/limit times retryTimeUnit = primary ? fFinalTimerTimeout : fWatchdogTimeout; // Note: addition of (retryTimeUnit / 2) is to round up for better accuracy disconnectLinkTime = fMyQOS->GetLinkDiscThresholdTime(); fDisconnectLinkLimit = (disconnectLinkTime + (retryTimeUnit >> 1)) / retryTimeUnit; // Calculate the BB duration time. Adjust for one retry at the beginning. fDisconnectLinkLimitTime = fDisconnectLinkLimit * retryTimeUnit - retryTimeUnit; if (disconnectLinkTime == kIrDisconnectWarningTimeout) { // Special case: warning time is 0 seconds if link disconnect at 3 seconds fDisconnectWarningLimit = 0; } else { // Note: addition of (retryTimeUnit / 2) is to round up for better accuracy fDisconnectWarningLimit = ((kIrDisconnectWarningTimeout + (retryTimeUnit >> 1)) / retryTimeUnit) - 1; } // Set number of frames that can be sent to peer before giving up link fPeerWindowSize = (UByte)fPeerQOS->GetWindowSize(); // Set number of frames that peer can send to me before giving up link fMyWindowSize = (UByte)fMyQOS->GetWindowSize(); // Allocate receive buffers numBuffers = fMyQOS->GetWindowSize() * 2; // FIXME - post alpha allocate extra to avoid assert bufferSize = fMyQOS->GetDataSize()+5; // Add room for Addr, CNTL, CRC and 1 for DMA check(numBuffers <= (sizeof(fGetBuffers) / sizeof(fGetBuffers[0]))); // sanity check(numBuffers < 32); // bitmask allocation // Allocate buffers for receiving data for (fGetBufferAvail = 0, fNumGetBuffers = 0; fNumGetBuffers < numBuffers; fNumGetBuffers++) { // Allocate, init buffer segment and check for errors result = kIrDAErrNoMemory; bufferItem = CBufferSegment::New( bufferSize ); XREQUIRE(bufferItem, Fail_BufferItem_New); // Add next buffer list fGetBuffers[fNumGetBuffers] = bufferItem; fGetBufferAvail |= (ULong) (1 << fNumGetBuffers); } // First my info XTRACE(kQOSBufferInfoEvent, fMyQOS->GetWindowSize(), fMyQOS->GetDataSize()); XTRACE(kQOSLeadInCountEvent, 0, fMyQOS->GetExtraBOFs()); XTRACE(kQOSMinTurnAroundEvent, 0, fMyQOS->GetMinTurnAroundTime() / kMicroseconds); XTRACE(kQOSMaxTurnAroundEvent, 0, fMyQOS->GetMaxTurnAroundTime() / kMilliseconds); // Then the peer device's info XTRACE(kQOSBufferInfoEvent, fPeerQOS->GetWindowSize(), fPeerQOS->GetDataSize()); XTRACE(kQOSLeadInCountEvent, 1, fPeerQOS->GetExtraBOFs()); XTRACE(kQOSMinTurnAroundEvent, 1, fPeerQOS->GetMinTurnAroundTime() / kMicroseconds); XTRACE(kQOSMaxTurnAroundEvent, 1, fPeerQOS->GetMaxTurnAroundTime() / kMilliseconds); // Initialize connection state fVr = 0; fVs = 0; fWindow = fPeerWindowSize; fValidRecdNs = 0xFF >> (8 - fMyWindowSize); fRetryCount = 0; #if forMac fInitialRetryTime = 0; //fNextKickScc = 0; fInBrokenBeam = false; //gConnectionBroken = false; #endif fRemoteBusy = false; fLocalBusy = false; fSetLocalBusyPending = false; fClrLocalBusyPending = false; fRecdNr = 0; fRecdNs = 0; fNextToAck = 0; return noErr; // Error exits: //Fail_BufferItem_Init: // delete bufferItem; Fail_BufferItem_New: FreeGetBuffers(); Fail_QOS_NegotiatePeer: Fail_QOS_NegotiateMe: Fail_QOS_ExtractInfo: ConnLstnComplete(result); return result; } // TIrLAP::ParseNegotiateAndInitConnState //-------------------------------------------------------------------------------- // ConnLstnComplete //-------------------------------------------------------------------------------- void TIrLAP::ConnLstnComplete(IrDAErr result) { // Let the initiator know that the connect/listen has completed TIrConnLstnReply* connLstnReply = (TIrConnLstnReply*)fCurrentRequest; XTRACE(kConnLstnComplete, fPeerDevAddr >> 16, (short)fPeerDevAddr); fCurrentRequest = nil; //XASSERT(connLstnReply != nil); // jdg, this can happen if we're not doing much and get connected to if (connLstnReply) { // jdg - if there was a current request (can be listen, connect or discover) //XASSERT((connLstnReply->fEvent == kIrConnectRequestEvent) || (connLstnReply->fEvent == kIrListenRequestEvent)); if ((connLstnReply->fEvent == kIrConnectRequestEvent) || (connLstnReply->fEvent == kIrListenRequestEvent)) { connLstnReply->fEvent = (UByte)RequestIdToReplyId(connLstnReply->fEvent); connLstnReply->fPassiveConnect = (connLstnReply->fEvent == kIrConnectReplyEvent) && !fPrimary; connLstnReply->fResult = result; connLstnReply->fDevAddr = fPeerDevAddr; // Only listen needs this, already set for connect } else { // we have a request, but it's not listen or connect (probably discover). reject it. if (connLstnReply->fEvent == kIrDiscoverRequestEvent) { RejectRequest(connLstnReply, kIrDAErrRetry); // send it back with an error connLstnReply = nil; // don't do it again! nil out for below } else { XTRACE(kConnLstnComplete, (UInt16)-2, (UInt16)-2); // bug: can have a listen complete ... // just ignore it for now ... } } } else { XTRACE(kConnLstnComplete, (UInt16)-1, (UInt16)-1); // jdg: what to do here? } // Connect/listen failed? if (result != noErr) { fState = kIrLAPDisconnectedState; } if (connLstnReply) // jdg GetLMP->EnqueueEvent(connLstnReply); } // TIrLAP::ConnLstnComplete //-------------------------------------------------------------------------------- // DisconnectComplete //-------------------------------------------------------------------------------- void TIrLAP::DisconnectComplete(IrDAErr result) { // Its not good to complete disconnect with okay XASSERT(result != noErr); // Stop the Final/Poll/Watchdog timer (if it was on - nop if it wasn't) StopTimer(); // Strickly speaking this shouldn't be necessary if this has been called properly // But its a good idea to make sure that they're really "stopped" before shutting down // At least check to see if calling code has some logic errors/flaws/omissions/opportunities XASSERT(!fInputInProgress); XASSERT(!fOutputInProgress); StopInput(); StopOutput(); // Complete all in progress and pending put requests CancelPendingPutRequests(nil, kIrDAErrCancel); // Reset some state in case we are re-connected to. #if forMac fInBrokenBeam = false; //gConnectionBroken = false; fConnected = false; // JDG. we're no longer connected fNickName[0] = 0; // Clear out the connect name. #endif fLocalBusy = false; fSetLocalBusyPending = false; fEnteringCloseState = false; fFRMRPending = false; //fNeedNewInputBuffer = true; // jdg - this is now cleared in FreeGetBuffers fState = kIrLAPDisconnectedState; // Notify initiator of disconnect or notify clients via async event response if (fPendingDisconnect) { TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)fPendingDisconnect; fPendingDisconnect = nil; disconnectReply->fEvent = kIrDisconnectReplyEvent; disconnectReply->fResult = result; XTRACE(kNRMDisconnectReply, fState, 0); GetLMP->EnqueueEvent(disconnectReply); fIrDA->Disconnected(false); // tell glue to reset qos only Reset(); // JDG: FreeGetBuffers (else memory leak on next connection) } else { // async disconnect XTRACE(kNRMDisconnectAsyncReply, fState, 0); // FIXME - This doesn't look good for mac fIrDA->Disconnected(true); // tell glue to reset qos and lap and lmp and lapconn ... } // Reset back to NDM state (9600bps, disconnected, etc.) ApplyDefaultConnParms(); // jdg: now that we're back to normal, requeue any requests that came in while // we were waiting for the close state to exit if (fPendingRequests && !fPendingRequests->Empty()) { CListIterator *iter = CListIterator::cListIterator(fPendingRequests); TIrEvent *request; for (request = (TIrEvent*)iter->FirstItem(); iter->More(); request = (TIrEvent*)iter->NextItem()) { XTRACE(kNRMDisconnectRequeue,0, request->fEvent); // Send the reply to ourselves this->EnqueueEvent(request); } iter->release(); while (!fPendingRequests->Empty()) // now let's purge the list fPendingRequests->RemoveLast(); } } // TIrLAP::DisconnectComplete //-------------------------------------------------------------------------------- // CancelPutRequest //-------------------------------------------------------------------------------- void TIrLAP::CancelPutRequest() { TIrCancelPutRequest* cancelPutRequest = (TIrCancelPutRequest*)GetCurrentEvent(); // Cancel all pending (or in progress) put requests for the lsapConn which made the request CancelPendingPutRequests(cancelPutRequest->fLSAPConn, kIrDAErrRequestCanceled); // Respond to the request, using the request block for the response cancelPutRequest->fEvent = kIrCancelPutReplyEvent; cancelPutRequest->fResult = noErr; cancelPutRequest->fLSAPConn->EnqueueEvent(cancelPutRequest); } // TIrLAP::CancelPutRequest //-------------------------------------------------------------------------------- // CancelPendingPutRequests //-------------------------------------------------------------------------------- void TIrLAP::CancelPendingPutRequests(TLSAPConn* lsapConn, IrDAErr returnCode) { FastInt index; TIrPutReply* putReply; // Reply to all in-progress put requests for (index = 0; index < 8; index++) { putReply = (TIrPutReply*)fPutRequests[index]; if ((putReply != nil) && ((lsapConn == nil) || (lsapConn == putReply->fLSAPConn))) { fPutRequests[index] = nil; PutComplete(putReply, returnCode); } } // Reply to all pending put requests (must remove from end, since holes will be created, and its faster) for (index = fPendingPutRequests->GetArraySize() - 1; index >= 0 ; index--) { putReply = (TIrPutReply*)fPendingPutRequests->At(index); if ((putReply != nil) && ((lsapConn == nil) || (lsapConn == putReply->fLSAPConn))) { fPendingPutRequests->RemoveAt(index); PutComplete(putReply, returnCode); } } } // TIrLAP::CancelPendingPutRequests //-------------------------------------------------------------------------------- // PutComplete //-------------------------------------------------------------------------------- void TIrLAP::PutComplete(TIrPutReply* putReply, IrDAErr result) { // Put completes happen in a couple of places, so I consolidated them here // A special case: the peer device acknowledges the put after it was cancelled. // In this case the caller (UpdateNrReceived) would pass nil, since CancelPutRequests // was already called and set the pending put request to nil. So just ignore it. if (putReply == nil) { // Nothing to do, man } // If fLSAPConn is non-nil, send back put reply else if (putReply->fLSAPConn != nil) { putReply->fEvent = kIrPutDataReplyEvent; putReply->fResult = result; putReply->fLSAPConn->EnqueueEvent(putReply); } // If fLSAPConn is nil, don't reply - just release the put request block else { fIrDA->ReleaseEventBlock(putReply); } } // TIrLAP::PutComplete //-------------------------------------------------------------------------------- // NotConnectedCompletion //-------------------------------------------------------------------------------- void TIrLAP::NotConnectedCompletion() { // A request has been received while either before getting connected or more // likely some time after a disconnect has begun. [Most likely its a put request]. TIrLSAPConnEvent* request = (TIrLSAPConnEvent*)GetCurrentEvent(); // Simple reply to the request with an error. switch (request->fEvent) { case kIrPutDataRequestEvent: request->fEvent = kIrPutDataReplyEvent; break; default: DebugLog("TIrLAP::NotConnectedCompletion: unexpected request"); break; } request->fResult = kIrDAErrNotConnected; request->fLSAPConn->EnqueueEvent(request); } // TIrLAP::NotConnectedCompletion //-------------------------------------------------------------------------------- // ApplyDefaultConnParms //-------------------------------------------------------------------------------- void TIrLAP::ApplyDefaultConnParms() { // Change baud rate back to negotiation baud rate GetIrDevice->ChangeSpeed(k9600bps); // Set lead-in count back to default fLeadInCount = kIrDefaultLeadinCount; } // TIrLAP::ApplyDefaultConnParms //-------------------------------------------------------------------------------- // StartDataReceive //-------------------------------------------------------------------------------- void TIrLAP::StartDataReceive() { CBufferSegment* inputBuffer; // fIOBufferItem should be used only for control and when local busy // is in effect. else we should have a GetBuffer available for a // normal data read. GetBuffers are the only ones valid to be passed // up the stack (and returned via ReleaseInputBuffer). if (fInputBuffer == fIOBufferItem) // if currently using control buffer (or busy) fInputBuffer = nil; // reset it to switch to getbuffers if (fInputBuffer) { // if non-nil, then we already have a getbuffer // and reuse the current input buffer inputBuffer = fInputBuffer; XTRACE( kReusingBuffer, (int)fInputBuffer >> 16, (short)fInputBuffer ); } else { ULong flags; // better not have more than 32 buffers ULong index; // Set default in case no available buffer was found inputBuffer = fIOBufferItem; // use this if "local busy" condition // Find and use an available buffer for (index = 0, flags = 1; index < fNumGetBuffers; index++, flags <<= 1) { if (fGetBufferAvail & flags) { // if this buffer free fGetBufferAvail &= ~flags; // allocate the buffer inputBuffer = fGetBuffers[index]; //fNeedNewInputBuffer = false; XTRACE(kLogStartDataRcv1, (int)inputBuffer >> 16, (short)inputBuffer); XTRACE(kLogStartDataRcv2, flags, index); break; } } } if (inputBuffer == fIOBufferItem ) // debugging only XTRACE( kUsingDefaultBuffer, (int)fIOBufferItem >> 16, (short)fIOBufferItem ); require(inputBuffer, Bogus); StartInput(inputBuffer); Bogus: return; } // TIrLAP::StartDataReceive //-------------------------------------------------------------------------------- // ReleaseInputBuffer //-------------------------------------------------------------------------------- void TIrLAP::ReleaseInputBuffer(CBufferSegment* inputBuffer) { // This is called when the multiplexor has passed the inputbuffer to a client // and is returning the buffer to the buffer pool. The buffer is marked as available. ULong flags; ULong index; Boolean bufferFound = false; XTRACE(kLogReleaseInputBuffer, (int)inputBuffer >> 16, (short)inputBuffer); // Find the buffer for (index = 0, flags = 1; index < fNumGetBuffers; index++, flags <<= 1) { if (fGetBuffers[index] == inputBuffer) { // Check for releasing a buffer twice XASSERT((fGetBufferAvail & flags) == 0); fGetBufferAvail |= flags; bufferFound = true; XTRACE(kLogReleaseInputBuffer2, flags, index); } } //XASSERT(bufferFound); // It should be one of the buffers, require(bufferFound, BufferNotFound); // but don't croak if its not // Clear localBusy condition before it gets reported if (fSetLocalBusyPending) { XTRACE(kAbortLocalBusyPendingEvent, 0, 0); XASSERT(!fLocalBusy); fSetLocalBusyPending = false; } // Clear localBusy condition if we were in a local busy condition and just now got out of it else if (fLocalBusy && !fClrLocalBusyPending) { XTRACE(kClrLocalBusyPendingEvent, 0, 0); fLocalBusyClearedEvent->fEvent = kIrLocalBusyClearedEvent; fLocalBusyClearedEvent->fResult = noErr; fClrLocalBusyPending = true; this->EnqueueEvent(fLocalBusyClearedEvent); } BufferNotFound: return; } // TIrLAP::ReleaseInputBuffer //-------------------------------------------------------------------------------- // PostponePutRequest //-------------------------------------------------------------------------------- void TIrLAP::PostponePutRequest() { fPendingPutRequests->InsertFirst(GetCurrentEvent()); } // TIrLAP::PostponePutRequest //-------------------------------------------------------------------------------- // PrepareFRMRResponse //-------------------------------------------------------------------------------- void TIrLAP::PrepareFRMRResponse() { // Save info necessary when the FRMR can be sent fFRMRRejCtrlField = fRecdCtrl; fFRMRMyNrAndNs = (fVr << kIrLAPNrShift) | (fVs << kIrLAPNsShift) | (fRecdCR ? kIrFRMRCrBit : 0); if (RecdInvalidNr()) { fFRMRReasonFlags = kIrFRMRFlagInvalidNrCount; } else if (RecdInvalidNs()) { fFRMRReasonFlags = 0; } else { fFRMRReasonFlags = kIrFRMRFlagUndefinedCtrl; } // Set flag indicating that frmr is pending fFRMRPending = true; } // TIrLAP::PrepareFRMRResponse //-------------------------------------------------------------------------------- // OutputXIDCommand //-------------------------------------------------------------------------------- void TIrLAP::OutputXIDCommand() { ULong deviceInfoLength = 0; TIrDscInfo* myDscInfo; TXIDPacket* xidCmd = (TXIDPacket*)fIOBufferItem->GetBufferPtr(); XTRACE(kDiscoverXIDCmdEvent, fDiscoverSlot, 0); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPCmdXID; // Fill out the discover XID frame xidCmd->fAddress = (kIrLAPBroadcastAddr << 1) | kIrLAPCommandBit; xidCmd->fCmdRsp = kIrLAPCmdXID | kIrLAPPollBit; xidCmd->fFormatId = kIrLAPDiscoveryXIDFormat; xidCmd->fSrcDevAddr = fMyDevAddr; xidCmd->fDstDevAddr = fConflictDevAddr; xidCmd->fFlags = fDiscoverFlags; xidCmd->fSlotNum = fDiscoverSlot; xidCmd->fVersion = kIrLAPVersionNumber; // Add the caller supplied discovery info on final. if (fDiscoverSlot == kIrLAPFinalSlot) { // Get my discovery info (PDA/"Newton" unless changed by client) #if forMac myDscInfo = GetDiscovery->GetDiscoveryInfo(); #else myDscInfo = fIrDA->GetMyDscInfo(); #endif deviceInfoLength = myDscInfo->AddDevInfoToBuffer(&xidCmd->fDevInfo[0], kMaxUnconnectedPacketSize - kTXIDPacketSize); } // Set up the put buffer fPutBuffer->SetControlBuffer(&xidCmd->fAddress, kTXIDPacketSize + deviceInfoLength, true); // Output the discover XID frame check(fLeadInCount == kIrDefaultLeadinCount); // if not, how'd that happen? StartOutput(fPutBuffer, kIrDefaultLeadinCount ); // XID should have 10 BOFs per spec //#if forMac // Immediately post a read for the response packet. The driver // StartInput(&fIOBufferItem); // will queue it behind the XID write and it be posted to .XIn //#endif // when the XID completes. It takes too long for the stack to // process the Output complete and post the response read. } // TIrLAP::OutputXIDCommand //-------------------------------------------------------------------------------- // OutputXIDResponse //-------------------------------------------------------------------------------- void TIrLAP::OutputXIDResponse(TXIDPacket& xidCmd) { ULong deviceInfoLength; UByte xidFlags = xidCmd.fFlags; TIrDscInfo* myDscInfo; TXIDPacket* xidRsp = (TXIDPacket*)fIOBufferItem->GetBufferPtr(); XTRACE(kDiscoverXIDRspEvent, fDiscoverSlot, xidFlags); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPRspXID; // Give myself a new dev address if conflict resolution situation #if forMac if( xidFlags & kIrLAPDiscoverFlagsNewAddr ) fReplacementDevAddr = ( UInt32 ) ( random() % 0xFFFFFFFE + 1 ); else fReplacementDevAddr = 0; #else fReplacementDevAddr = xidFlags & kIrLAPDiscoverFlagsNewAddr ? (ULong)rand() % 0xFFFFFFFE + 1 : 0; #endif // Create a response XID frame xidRsp->fAddress = kIrLAPBroadcastAddr << 1; xidRsp->fCmdRsp = kIrLAPRspXID | kIrLAPFinalBit; xidRsp->fFormatId = kIrLAPDiscoveryXIDFormat; xidRsp->fSrcDevAddr = fReplacementDevAddr ? fReplacementDevAddr : fMyDevAddr; xidRsp->fDstDevAddr = xidCmd.fSrcDevAddr; // Response flags are essentially reflection of the flags received. // Mask off all but supported flags just in case some junk was received. xidRsp->fFlags = xidFlags & (kIrLAPDiscoverFlagsNewAddr | kIrLAPDiscoverFlagsSlotMask); xidRsp->fSlotNum = fDiscoverSlot; xidRsp->fVersion = kIrLAPVersionNumber; // Add the caller supplied discovery info. #if forMac myDscInfo = GetDiscovery->GetDiscoveryInfo(); #else myDscInfo = fIrDA->GetMyDscInfo(); #endif deviceInfoLength = myDscInfo->AddDevInfoToBuffer(&xidRsp->fDevInfo[0], kMaxUnconnectedPacketSize - kTXIDPacketSize); // Set up the put buffer fPutBuffer->SetControlBuffer(&xidRsp->fAddress, kTXIDPacketSize + deviceInfoLength, true); // Output the discover XID frame StartOutput(fPutBuffer, /* fLeadInCount */ 10); // CPD-XID should have 10 BOFs per spec } // TIrLAP::OutputXIDResponse //-------------------------------------------------------------------------------- // OutputSNRMCommand //-------------------------------------------------------------------------------- void TIrLAP::OutputSNRMCommand() { ULong qosDataLength; TSNRMPacket* snrmCmd = (TSNRMPacket*)fIOBufferItem->GetBufferPtr(); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPCmdSNRM; // Fill out the SNRM frame snrmCmd->fAddress = (kIrLAPBroadcastAddr << 1) | kIrLAPCommandBit; snrmCmd->fCmdRsp = kIrLAPCmdSNRM | kIrLAPPollBit; snrmCmd->fSrcDevAddr = fMyDevAddr; snrmCmd->fDstDevAddr = fPeerDevAddr; snrmCmd->fConnAddr = fConnAddr << 1; // Add negotiation info passed down to me qosDataLength = fMyQOS->AddInfoToBuffer(&snrmCmd->fQOSParmData[0], kMaxUnconnectedPacketSize - kTSNRMPacketSize); // Set up the put buffer fPutBuffer->SetControlBuffer(&snrmCmd->fAddress, kTSNRMPacketSize + qosDataLength, true); // Output the connect SNRM frame StartOutput(fPutBuffer, fLeadInCount); } // TIrLAP::OutputSNRMCommand //-------------------------------------------------------------------------------- // OutputUAResponse //-------------------------------------------------------------------------------- void TIrLAP::OutputUAResponse() { ULong qosDataLength; TUAPacket* uaRsp = (TUAPacket*)fIOBufferItem->GetBufferPtr(); #if forMac if (!fConnected) { // calling OutputUA more than once, tell discover only once fConnected = true; // jdg flag as connected a little earlier than before GetDiscovery->GetRemoteDeviceName( fPeerDevAddr, fNickName ); } #endif // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPRspUA; // Fill out the UA frame uaRsp->fAddress = fConnAddr << 1; uaRsp->fCmdRsp = kIrLAPRspUA | kIrLAPFinalBit; uaRsp->fSrcDevAddr = fMyDevAddr; uaRsp->fDstDevAddr = fPeerDevAddr; // Add negotiation info (passed down to me and negotiated against SNRM QOS parms) qosDataLength = fMyQOS->AddInfoToBuffer(&uaRsp->fQOSParmData[0], kMaxUnconnectedPacketSize - kTUAPacketSize); // Set up the put buffer fPutBuffer->SetControlBuffer(&uaRsp->fAddress, kTUAPacketSize + qosDataLength, true); XTRACE(kOutputUACommandEvent, uaRsp->fCmdRsp, uaRsp->fAddress ); XTRACE(kOutputUACommandEvent, qosDataLength >> 16, qosDataLength ); // testing // Output the connect UA response frame StartOutput(fPutBuffer, fLeadInCount); } // TIrLAP::OutputUAResponse //-------------------------------------------------------------------------------- // OutputFRMRResponse //-------------------------------------------------------------------------------- void TIrLAP::OutputFRMRResponse() { TFRMRPacket* frmrRsp = (TFRMRPacket*)fIOBufferItem->GetBufferPtr(); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPRspFRMR; // Fill out the FRMR frame frmrRsp->fAddress = fConnAddr << 1; frmrRsp->fCmdRsp = kIrLAPRspFRMR | kIrLAPFinalBit; frmrRsp->fRejCtrlField = fFRMRRejCtrlField; frmrRsp->fMyNrAndNs = fFRMRMyNrAndNs; frmrRsp->fReasonFlags = fFRMRReasonFlags; // Set up the put buffer fPutBuffer->SetControlBuffer(&frmrRsp->fAddress, kTFRMRPacketSize, true); // Output the FRMR response frame StartOutput(fPutBuffer, fLeadInCount); } // TIrLAP::OutputFRMRResponse //-------------------------------------------------------------------------------- // OutputControlFrame //-------------------------------------------------------------------------------- void TIrLAP::OutputControlFrame(UByte cmdRsp) { // This can be used to output simple U frames or S frames (not intended for I frames) // NOTE: The final/poll bit is always set by this. TControlPacket* frame = (TControlPacket*)fIOBufferItem->GetBufferPtr(); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = cmdRsp; // Fill out the control frame frame->fAddress = (fConnAddr << 1) | (fPrimary ? kIrLAPCommandBit : 0); frame->fCmdRsp = cmdRsp | kIrLAPPollBit; if ((cmdRsp & kIrLAPUSIMask) != kIrLAPUnnumbered) { frame->fCmdRsp |= fVr << kIrLAPNrShift; } if (cmdRsp == kIrLAPFrameRR) XTRACE(kOutputControlFrameRR, 0, fVr); else XTRACE(kOutputControlFrame, cmdRsp, fVr); // Set up the put buffer fPutBuffer->SetControlBuffer(&frame->fAddress, kTControlPacketSize, true); // Output the frame StartOutput(fPutBuffer, fLeadInCount); #if forMac switch( cmdRsp ) { // Log the packet type sent case kIrLAPFrameRR: GetIrDevice->Stats_RRSent(); break; case kIrLAPFrameRNR: GetIrDevice->Stats_RNRSent(); break; case kIrLAPFrameREJ: GetIrDevice->Stats_REJSent(); break; case kIrLAPFrameSREJ: GetIrDevice->Stats_SREJSent(); break; } #endif } // TIrLAP::OutputControlFrame //-------------------------------------------------------------------------------- // OutputDataFrame //-------------------------------------------------------------------------------- void TIrLAP::OutputDataFrame(TIrPutRequest* request, Boolean finalOrPollFlag) { ULong lmPDULength; UByte finalPollBit = finalOrPollFlag ? kIrLAPFinalBit : 0; // Note: final/poll bit same bit TControlPacket* frame = (TControlPacket*)fIOBufferItem->GetBufferPtr(); if (finalPollBit) XTRACE(kOutputDataFrame, fVs, fVr); else XTRACE(kOutputDataFrameNotFinal, fVs, fVr); // Set cmd output so output complete event knows what to do next fNextCmdRspToSend = kIrLAPFrameINFO; // Keep track of the send buffer in the resend/ack/reply array fPutRequests[fVs] = request; // Update the valid fRecdNr bits fValidRecdNr |= 1 << fVs; // Fill out the data frame header frame->fAddress = (fConnAddr << 1) | (fPrimary ? kIrLAPCommandBit : 0); frame->fCmdRsp = kIrLAPFrameINFO | finalPollBit | (fVr << kIrLAPNrShift) | (fVs << kIrLAPNsShift); // Advance the send index fVs = (fVs + 1) & 0x7; // Fill in the LM-PDU header (control or data LM-PDU info) lmPDULength = GetLMP->FillInLMPDUHeader(request, frame->fLMPDUData); // Set up the put buffer fPutBuffer->SetControlBuffer(&frame->fAddress, kTControlPacketSize + lmPDULength, true); fPutBuffer->SetDataBuffer(request->fData, request->fOffset, request->fLength); // Output the data frame StartOutput(fPutBuffer, (UInt32)fLeadInCount); #if forMac GetIrDevice->Stats_IFrameSent(); #endif } // TIrLAP::OutputDataFrame //-------------------------------------------------------------------------------- // GotData //-------------------------------------------------------------------------------- Boolean TIrLAP::GotData(UByte *data, ULong size) { ULong result; result = fInputBuffer->Getn(data, size); // try and get size bytes XTRACE(kLogGotData, size, result); // log requested/result counts return (result == size); //return (ULong)fInputBuffer->Getn(data, size) == size; } // TIrLAP::GotData //-------------------------------------------------------------------------------- // RecdCmd //-------------------------------------------------------------------------------- Boolean TIrLAP::RecdCmd(UByte cmdPattern) { return fRecdCR && (fRecdCmdRsp == cmdPattern); } // TIrLAP::RecdCmd //-------------------------------------------------------------------------------- // RecdPollCmd //-------------------------------------------------------------------------------- Boolean TIrLAP::RecdPollCmd(UByte cmdPattern) { return fRecdPF && RecdCmd(cmdPattern); } // TIrLAP::RecdPollCmd //-------------------------------------------------------------------------------- // RecdRsp //-------------------------------------------------------------------------------- Boolean TIrLAP::RecdRsp(UByte rspPattern) { return !fRecdCR && (fRecdCmdRsp == rspPattern); } // TIrLAP::RecdRsp //-------------------------------------------------------------------------------- // RecdFinalRsp //-------------------------------------------------------------------------------- Boolean TIrLAP::RecdFinalRsp(UByte rspPattern) { return fRecdPF && RecdRsp(rspPattern); } // TIrLAP::RecdFinalRsp //================================= Timer stuff ================================== //-------------------------------------------------------------------------------- // StartTimer //-------------------------------------------------------------------------------- void TIrLAP::StartTimer(TTimeout timeDelay, int refCon) { XTRACE(kLogStartTimer, timeDelay, refCon); fIrDA->StartTimer(kTimer_LAP, timeDelay, refCon); } // TIrLAP::StartTimer //-------------------------------------------------------------------------------- // StopTimer //-------------------------------------------------------------------------------- void TIrLAP::StopTimer() { /* fMediaBusyTimer. StopTimer(); // Stop them all to make sure. This is fine fDiscoverSlotTime. StopTimer(); // since the stack expects only one timer fFinalTimer. StopTimer(); // to be active at any given time, which is fBackOffTimer. StopTimer(); // not always the case. Real work is done only fWatchDogTimer. StopTimer(); // if the timer is active. fDiscoverMaxSlotTimer. StopTimer(); fMinTurnAroundTimer. StopTimer(); */ XTRACE(kLogStopTimer, 0, 0); fIrDA->StopTimer(kTimer_LAP); } // TIrLAP::StopTimer //-------------------------------------------------------------------------------- // TimerComplete //-------------------------------------------------------------------------------- void TIrLAP::TimerComplete(ULong refCon) { XTRACE(kLogTimerComplete, 0, refCon); check((refCon >= kIrFirstIrLAPTimerEvent) && (refCon <= kIrLastIrLAPTimerEvent)); NextState(refCon); } // TIrLAP::TimerComplete //============================= Input/Output stuff =============================== //-------------------------------------------------------------------------------- // StartOutput //-------------------------------------------------------------------------------- void TIrLAP::StartOutput(TIrLAPPutBuffer* outputBuffer, ULong leadInCount) { fOutputInProgress = true; fLastCmdRsp = fNextCmdRspToSend; // Stash the current packet type so it is valid // between sends. #if forMac GetIrDevice->StartTransmit(outputBuffer, leadInCount); #else fIrDA->StartTransmit(outputBuffer, leadInCount); #endif } // TIrLAP::StartOutput //-------------------------------------------------------------------------------- // StopOutput //-------------------------------------------------------------------------------- void TIrLAP::StopOutput() { fOutputInProgress = false; #if !forMac // Packet was completely written during StartOutput fIrDA->StopTransmit(); #endif } // TIrLAP::StopOutput //-------------------------------------------------------------------------------- // StartInput //-------------------------------------------------------------------------------- void TIrLAP::StartInput(CBufferSegment* inputBuffer) { XTRACE(kLogStartInput, (int)inputBuffer >> 16, (short)inputBuffer); XTRACE(kLogStartInput2, (int)fInputBuffer >> 16, (short)fInputBuffer); require(inputBuffer, Bogus); // shouldn't ever happen //Boolean localBusy = fLocalBusy || fSetLocalBusyPending; // jdg hack #832 // if fInputBuffer is non-nil and not fIOBufferItem and not the same as what // was used last time, then it's one of the get buffers and we need to "release" // it so it can be safely cleaned up by FreeGetBuffers. Could skip this except for // the sanity checks in FreeGetBuffers. if (fInputBuffer && fInputBuffer != fIOBufferItem && inputBuffer != fInputBuffer) { XTRACE(kLogStartInput3, 0xfeed, 0xbeef); require(inputBuffer == fIOBufferItem, SanityLacks); //DebugLog("working to avoid buffer leak in StartInput, release on 0x%lx\n", (UInt32) fInputBuffer); ReleaseInputBuffer(fInputBuffer); // could queue clear local busy event... should be ok } SanityLacks: fInputInProgress = true; fInputBuffer = inputBuffer; // remember input buffer inputBuffer->Reset(); // Start the ball rolling // Used to tell the i/o guy to just return the afield and cfield if we've run out // of buffers, but now that fIOBufferItem is a full-sized CBufferSegment we // go ahead and always accept a full-size packet anyway. Shouldn't ever // get back more than the header anyway. GetIrDevice->StartReceive(inputBuffer); Bogus: return; } // TIrLAP::StartInput //-------------------------------------------------------------------------------- // StopInput //-------------------------------------------------------------------------------- void TIrLAP::StopInput() { fInputInProgress = false; GetIrDevice->StopReceive(); } // TIrLAP::StopInput /* //-------------------------------------------------------------------------------- // InputHappening //-------------------------------------------------------------------------------- Boolean TIrLAP::InputHappening() { // Are we receiving data even though receipt may not be complete? return fIrSIR->Stats_ReceivingData(); } // TIrLAP::InputHappening */ //-------------------------------------------------------------------------------- // OutputComplete //-------------------------------------------------------------------------------- void TIrLAP::OutputComplete() { fOutputInProgress = false; #ifdef IRDA_TEST_FRAME_SUPPORTED if (fHandlingTestFrame) { TestFrameComplete(); } else #endif { XTRACE( kPacketOutput, 0, 0 ); NextState(kIrOutputCompleteEvent); } } // TIrLAP::OutputComplete //-------------------------------------------------------------------------------- // ChangeSpeedComplete //-------------------------------------------------------------------------------- void TIrLAP::ChangeSpeedComplete() { // only do the ChangeSpeedComplete event in the two states that handle it, otherwise // this routine is a nop (i.e. called from one of the paths to DisconnectComplete) if (fState == kIrLAPConnectState || fState == kIrLAPListenState) NextState(kIrChangeSpeedCompleteEvent); else { XTRACE(kLogChangeSpeedCompleteIgnored, 0, 0); } } // TIrLAP::ChangeSpeedComplete //-------------------------------------------------------------------------------- // InputAborted -- input aborted, pretend we timed out (but faster) // returns true if the 'abort' worked (else the caller shoujld // hang another read out) //-------------------------------------------------------------------------------- Boolean TIrLAP::InputAborted() { XTRACE(kInputAborted, 0, 0); if (fState == kIrLAPPriReceiveState && // sanity fMyWindowSize == 1) { // if aborted read and my windowsize == 1 StartTimer(fMinTurnAroundTimeout, kIrFinalTimerExpiredEvent); return true; } return false; } //-------------------------------------------------------------------------------- // InputComplete //-------------------------------------------------------------------------------- void TIrLAP::InputComplete(UByte aField, UByte cField) { // Note: The aField and cField were extracted by the low level input routine (vs being // added to the input buffer) because the IrLAP spec specifies that the negotiated data // size is the maximum bytes in the I field of a frame, which does not leave room for the // aField or the cField. //fInputInProgress = false; // jdg - use as a read complete "valid" flag below // Extract the c/r bit from aField and normalize the address fRecdCR = aField & kIrLAPCommandBit; fRecdAddr = aField >> 1; // Extract the PF bit from cField fRecdPF = cField & kIrLAPPFMask; UByte nrMask = 0; UByte nsMask = 0; fNrNsFlags = 0; // Only set (change from its previous value) fRecdNr if S or I frame if ((cField & kIrLAPUSIMask) != kIrLAPUnnumbered) { nrMask = kIrLAPNrMask; fRecdNr = (cField & nrMask) >> kIrLAPNrShift; if (fRecdNr != fVs) { fNrNsFlags |= kIrLAPUnexpectedNr; XTRACE(kUnexpectedNr, fRecdNr, fVs); // jdg if ((fValidRecdNr & (1 << fRecdNr)) == 0) { fNrNsFlags |= kIrLAPInvalidNr; XTRACE(kInvalidNr, fValidRecdNr, fRecdNr); // jdg } } } // Only set (change from its previous value) fRecdNs if I frame if ((cField & kIrLAPIMask) == kIrLAPInformation) { nsMask = kIrLAPNsMask; fRecdNs = (cField & nsMask) >> kIrLAPNsShift; if (fRecdNs != fVr) { fNrNsFlags |= kIrLAPUnexpectedNs; XTRACE(kUnexpectedNs, fRecdNs, fVr); // jdg if ((fValidRecdNs & (1 << fRecdNs)) == 0) { fNrNsFlags |= kIrLAPInvalidNs; XTRACE(kInvalidNs, fValidRecdNs, fRecdNs); // jdg } } } // Mask off P/F, Nr and Ns from cField to get received command fRecdCmdRsp = cField & ~(kIrLAPPFMask | nrMask | nsMask); // jdg ... now that that's set, let's do some debugging if (RecdSFrame()) { // supervisor, only Nr valid if (RecdPollOrFinal()) { if (fRecdCmdRsp == kIrLAPFrameRR) XTRACE(kInputControlFrameRR, 0, fRecdNr); else XTRACE(kInputControlFrame, fRecdCmdRsp, fRecdNr); } else { XTRACE(kInputControlFrameNotFinal, cField, fRecdNr); // possible?? } } else if (RecdIFrame()) { // data, both Ns and Nr valid #if forMac GetIrDevice->Stats_IFrameRec(); #endif if (RecdPollOrFinal()) XTRACE(kInputDataFrame, fRecdNs, fRecdNr); else { XTRACE(kInputDataFrameNotFinal, fRecdNs, fRecdNr); } } else if( RecdUFrame() ) { GetIrDevice->Stats_UFrameRec(); } /// end jdg add // Save the control field as is for FRMR error reporting fRecdCtrl = cField; // Only accept the input if broadcast or for me // Should have been verified by the input routine XASSERT ((fRecdAddr == kIrLAPBroadcastAddr) || (fRecdAddr == fConnAddr)); #ifdef IRDA_TEST_FRAME_SUPPORTED if (fRecdCmdRsp == kIrLAPCmdTEST) { // Handle test frame as a special case (man oh man, I hate special cases) HandleTestFrame(); } else #endif if (fInputInProgress) { // Only if we're expecting a read complete ... XTRACE( kValidPacketReceived, fState, 0 ); fInputInProgress = false; // now we don't have a read pending anymore NextState(kIrInputCompleteEvent); } else { // on my iMac, with a USB IrDA Pod, I get this every time the disk spins // back *up*. The deferred tasks are held for maybe 3 seconds, more than // long enough to fire the fTimer -- while a completed read is in the "queue" // waiting for us to handle. Could fix this at the IrDA USB layer, but the // general solution is to not accept packets here unless a read is still pending. XTRACE(kLogPacketDropped, 0, 0); //DebugLog("irlap ignoring packet;g"); } } // TIrLAP::InputComplete #ifdef IRDA_TEST_FRAME_SUPPORTED //-------------------------------------------------------------------------------- // HandleTestFrame //-------------------------------------------------------------------------------- void TIrLAP::HandleTestFrame() { XTRACE(kTestFrameReceivedEvent, fRecdAddr, 0); // If the frame was either sent to my connection or broadcast to me or anyone then send a response ULong testFrameLength = fInputBuffer->GetSize(); ULong* testFrame = (ULong*)fInputBuffer->GetBufferPtr(); if ((fRecdAddr == fConnAddr) || ((testFrameLength >= 8) && ((testFrame[1] == fMyDevAddr) || (testFrame[1] == kIrLAPSniffeeDevAddr) || (testFrame[1] == kIrLAPBroadcastDevAddr)))) { // By typical standards this appears to be a test frame for me, but still need // to check for special considerations when its for Sniffee's and I'm listening - // In that case do not respond, as Sniffers are only interested in non-listeners. if ((testFrame[1] == kIrLAPSniffeeDevAddr) && (fCurrentRequest != nil) && (fCurrentRequest->fEvent == kIrListenRequestEvent)) { TestFrameComplete(); } // It's a test frame for me or broadcast to anyone and not a sniff special - else { fHandlingTestFrame = true; // Build the header fTestHeader.fAddress = (fRecdAddr << 1) | (fPrimary ? kIrLAPCommandBit : 0); fTestHeader.fCmdRsp = kIrLAPRspTEST | kIrLAPFinalBit; // Swap the source and destination addresses if broadcast if (fRecdAddr == kIrLAPBroadcastAddr) { // Source becomes the destination testFrame[1] = testFrame[0]; // And I'm now the source testFrame[0] = fMyDevAddr; } // Set up the put buffer fPutBuffer->SetControlBuffer(&fTestHeader.fAddress, kTTestHdrPacketSize, true); fPutBuffer->SetDataBuffer(fInputBuffer, 0, testFrameLength); // Output the data frame StartOutput(fPutBuffer, fLeadInCount); } } // Test frame wasn't for me, so continue inputting in the simplest/least code way else { TestFrameComplete(); } } // TIrLAP::HandleTestFrame #endif #ifdef IRDA_TEST_FRAME_SUPPORTED //-------------------------------------------------------------------------------- // TestFrameComplete //-------------------------------------------------------------------------------- void TIrLAP::TestFrameComplete() { XTRACE(kTestFrameOutputDoneEvent, 0, 0); // Output of the test frame response has completed. fHandlingTestFrame = false; // Restart whatever input was pending and continue on (as you were private!). StartInput(fInputBuffer); } // TIrLAP::TestFrameComplete #endif // // JDG hacking. Currently used in HandleDisconnectedStateEvent to reject // pending requests rather than croak. Needs some cleanup. // void TIrLAP::RejectRequest(TIrEvent *request, IrDAErr err) { XTRACE(kRejectRequest, request->fEvent, err); request->fEvent = (UByte)RequestIdToReplyId(request->fEvent); request->fResult = err; GetLMP->EnqueueEvent(request); } #if forMac //-------------------------------------------------------------------------------- // LAPTimerNotifier: Completion proc for both timers //-------------------------------------------------------------------------------- void LAPTimerNotifier( UInt32 refCon, UInt32 sig ) { check( refCon ); TIrLAP * obj = ( TIrLAP * )refCon; obj->TimerComplete( sig ); } //=============================== TIrLAPPutBuffer ================================ #endif #undef super #define super OSObject OSDefineMetaClassAndStructors(TIrLAPPutBuffer, OSObject); //-------------------------------------------------------------------------------- // TIrLAPPutBuffer //-------------------------------------------------------------------------------- /*static*/ TIrLAPPutBuffer * TIrLAPPutBuffer::tIrLAPPutBuffer() { TIrLAPPutBuffer *obj = new TIrLAPPutBuffer; if (obj && !obj->init()) { obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------------- bool TIrLAPPutBuffer::init() { if (!super::init()) return false; fCtrlBuf = nil; fCtrlBufLength = 0; fCtrlBufPos = 0; fDataBuf = nil; fDataBufOffset = 0; fDataBufLength = 0; fDataBufPos = 0; return true; } // TIrLAPPutBuffer::init //-------------------------------------------------------------------------------- // Free //-------------------------------------------------------------------------------- void TIrLAPPutBuffer::free(void) { super::free(); } //-------------------------------------------------------------------------------- // SetControlBuffer //-------------------------------------------------------------------------------- void TIrLAPPutBuffer::SetControlBuffer(UByte* buffer, ULong length, Boolean initFirst) { if (initFirst) { init(); } fCtrlBuf = buffer; fCtrlBufLength = length; fCtrlBufPos = 0; } // TIrLAPPutBuffer::SetControlBuffer //-------------------------------------------------------------------------------- // SetDataBuffer //-------------------------------------------------------------------------------- void TIrLAPPutBuffer::SetDataBuffer(CBuffer* buffer, ULong offset, ULong length) { fDataBuf = buffer; fDataBufOffset = offset; fDataBufLength = length; fDataBufPos = 0; } // TIrLAPPutBuffer::SetDataBuffer //-------------------------------------------------------------------------------- // Get //-------------------------------------------------------------------------------- UByte TIrLAPPutBuffer::Get(void) { UByte nextChar; if (fCtrlBufPos < fCtrlBufLength) { XASSERT(fCtrlBuf); nextChar = fCtrlBuf[fCtrlBufPos++]; } else if (fDataBufPos < fDataBufLength) { nextChar = fDataBuf->Get(); fDataBufPos++; } else { DebugLog("TIrLAPPutBuffer::Get: getting past end of buffer(s)"); nextChar = 0xFF; } return nextChar; } // TIrLAPPutBuffer::Get //-------------------------------------------------------------------------------- // Seek //-------------------------------------------------------------------------------- void TIrLAPPutBuffer::Seek(Long off, int dir) { #pragma unused(off) // Low-rent version only supports whats needed XASSERT(((dir == kPosBeg) && (off == 0)) || ((dir == kPosCur) && (off == -1))); // If direction is kPosBeg, then this is a seek to 0 if (dir == kPosBeg) { fCtrlBufPos = 0; if (fDataBuf) { fDataBuf->Seek(fDataBufOffset, kPosBeg); fDataBufPos = 0; } } // Otherwise the only other supported option is to seek back to the previous character else { if (fDataBufPos) { // If fDataBufPos > 0 then must be getting chars from there, back up one XASSERT(fDataBuf); fDataBuf->Seek(-1, kPosCur); fDataBufPos--; } else if (fCtrlBufPos) { fCtrlBufPos--; } } } // TIrLAPPutBuffer::Seek //-------------------------------------------------------------------------------- // AtEOF //-------------------------------------------------------------------------------- Boolean TIrLAPPutBuffer::AtEOF(void) const { return (fCtrlBufPos == fCtrlBufLength) && (fDataBufPos == fDataBufLength); } // TIrLAPPutBuffer::AtEOF