// There can be a max of two simultaneous MO-SMS. // The CM Service Request to start a new MO-SMS during an existing one may arrive before the final ACK of the previous MO-SMS, as per GSM 4.11 5.4 // Therefore there are two MO-SMS possible: MMContext::TE_MOSMS1 is the old one and TE_MOSMS2 is the new one. void startMOSMS(const GSM::L3MMMessage *l3msg, MMContext *mmchan) { LOG(DEBUG) <<mmchan; //now we allocate below: TranEntry *tran = TranEntry::newMO(dcch, L3CMServiceType::ShortMessage); //MMContext *set = dcch->chanGetContext(true); RefCntPointer<TranEntry> prevMOSMS = mmchan->mmGetTran(MMContext::TE_MOSMS1); if (! prevMOSMS.self()) { // happiness. //set->setTran(MMContext::TE_MOSMS1,tran); //tran->teConnectChannel(dcch,TE_MOSMS1); } else { // Check for perfidy on the part of the MS: it cannot start a new MO-SMS unless the previous is nearly finished. //SmsState smsState = getCurrentSMSState(); MachineBase *base = prevMOSMS->currentProcedure(); bool badbunny = false; if (base) { // This may happen if the MS is a bad bunny and sends two CM Sevice Requests effectively simultaneously. MOSMSMachine *smssm = dynamic_cast<typeof(smssm)>(base); if (! smssm || smssm->mSmsState != MoSmsWaitForAck) { badbunny = true; } } else { badbunny = true; } if (badbunny) { LOG(ERR) << "Received new MO-SMS before previous MO-SMS completed"<<LOGVAR2("MOSMS",prevMOSMS.self())<<l3msg; // Now what? We've already got an SMS running... return; // Just ignore it. } RefCntPointer<TranEntry> prevMOSMS2 = mmchan->mmGetTran(MMContext::TE_MOSMS2); if (prevMOSMS2.self()) { LOG(ERR) <<"Received third simultaneous MO-SMS, which is illegal:"<<LOGVAR2("MO-SMS1",prevMOSMS.self())<<LOGVAR2("MO-SMS2",prevMOSMS2.self()); // Now what? We could kill the oldest one or reject the new one. // Kill the oldest one, on the assumption that this indicates a bug in our code and that SMS is hung. prevMOSMS->teCancel(TermCause::Local(L3Cause::SMS_Error)); // Promotes TE_MOSMS2 to TE_MOSMS1 devassert(mmchan->mmGetTran(MMContext::TE_MOSMS2) == NULL); } //mmchan->setTran(MMContext::TE_MOSMS2,tran); //tran->teConnectChannel(mmchan,MMContext::TE_MOSMS2); } TranEntry *tran = TranEntry::newMOSMS(mmchan); // Fire up an SMS state machine for this transaction. MOSMSMachine *mocp = new MOSMSMachine(tran); // The message is CMServiceRequest. tran->lockAndStart(mocp,(GSM::L3Message*)l3msg); }
// How did we get here you ask? Peering received a handover request on BTS2 (us), allocated a channel and set the handoverPending flag, // created a transaction with the specified IMSI, returned an L3 handover command which BTS1 sent to the MS, which then // sent a handover access to BTS2, and here we are! void ProcessHandoverAccess(L3LogicalChannel *chan) { using namespace SIP; // In this function, we are "BS2" in the ladder diagram. // This is called from the DCCH dispatcher when it gets a HANDOVER_ACCESS primtive. // The information it needs was saved in the transaction table by SaveHandoverAccess. LOG(DEBUG) << *chan; RefCntPointer<TranEntry> tran = chan->chanGetVoiceTran(); if (tran == NULL) { LOG(WARNING) << "handover access with no inbound transaction on " << chan; chan->chanRelease(L3_HARDRELEASE_REQUEST,TermCause::Local(L3Cause::Handover_Error)); return; } LOG(DEBUG) << *tran; if (!tran->getHandoverEntry(false)) { LOG(WARNING) << "handover access with no inbound handover on " << *chan; chan->chanRelease(L3_HARDRELEASE_REQUEST,TermCause::Local(L3Cause::Handover_Error)); return; } // clear handover in transceiver and get the RSSI and TE. // This instructs L2 to stop looking for and stop sending HANDOVER_ACCESS. // However, we cant just flush them out of the queue here because that is running in another // thread and it may keep pushing HANDOVER_ACCESS at, so we keep flushing them (below) // However, we should NEVER see HANDOVER_ACCESS after the ESTABLISH, yet I did. GSM::HandoverRecord hr = chan->getL2Channel()->handoverPending(false,0); // TODO: Move this into L1? if (hr.mhrTimingError > gConfig.getNum("GSM.MS.TA.Max")) { // Handover failure. LOG(NOTICE) << "handover failure on due to TA=" << hr.mhrTimingError << " for " << *tran; // RR cause 8: Handover impossible, timing advance out of range abortInboundHandover(tran,L3RRCause::Handover_Impossible,dynamic_cast<L2LogicalChannel*>(tran->channel())); chan->chanRelease(L3_HARDRELEASE_REQUEST,TermCause::Local(L3Cause::Distance)); // TODO: Is this right? Will the channel be immediately re-available? return; } chan->getL2Channel()->l1InitPhy(hr.mhrRSSI,hr.mhrTimingError,hr.mhrTimestamp); // Respond to handset with physical information until we get Handover Complete. int TA = (int)(hr.mhrTimingError + 0.5F); if (TA<0) TA=0; if (TA>62) TA=62; // We want to do this loop carefully so we exit as soon as we get a frame that is not HANDOVER_ACCESS. Z100Timer T3105(gConfig.GSM.Timer.T3105); // It defaults to only 50ms. // 4.08 11.1.3 "Ny1: The maximum number of repetitions for the PHYSICAL INFORMATION message during a handover." for (unsigned sendCount = gConfig.getNum("GSM.Handover.Ny1"); sendCount > 0; sendCount--) { T3105.set(); // (pat) It is UNIT_DATA because the channel is not established yet. // (pat) WARNING: This l3sendm call is not blocking because it is sent on FACCH which has a queue. // Rather than modifying the whole LogicalChannel stack to have a blocking mode, // we are just going to wait afterwards. The message should take about 20ms to transmit, // and GSM uses roughly 4 out of every 5 frames, so 20-25ms would transmit the message continuously. chan->l3sendm(L3PhysicalInformation(L3TimingAdvance(TA)),GSM::L3_UNIT_DATA); // (pat) Throw away all the HANDOVER_ACCESS that arrive while we were waiting. // They are not messages that take 4 bursts; they can arrive on every burst, so there // can be a bunch of them queued up (I would expect 5) for each message we send. while (L3Frame *frame = chan->l2recv(T3105.remaining())) { switch (frame->primitive()) { case HANDOVER_ACCESS: // See comments above. L2 is no longer generating these, but we need // to flush any extras from before we started, and there also might be have been // some in progress when we turned them off, so just keep flushing. LOG(INFO) << "flushing HANDOVER_ACCESS while waiting for Handover Complete"; delete frame; continue; case L3_ESTABLISH_INDICATION: delete frame; // Channel is established, so the MS is there. Finish up with a state machine. startInboundHandoverMachine(tran.self()); return; default: // Something else? LOG(NOTICE) << "unexpected primitive waiting for Handover Complete on " << *chan << ": " << *frame << " for " << *tran; delete frame; abortInboundHandover(tran,L3RRCause::Message_Type_Not_Compapatible_With_Protocol_State,chan); chan->chanRelease(L3_HARDRELEASE_REQUEST,TermCause::Local(L3Cause::Handover_Error)); // TODO: Is this right? Will the channel be immediately re-available? return; } } } // Failure. LOG(NOTICE) << "timed out waiting for Handover Complete on " << *chan << " for " << *tran; // RR cause 4: Abnormal release, no activity on the radio path abortInboundHandover(tran,L3RRCause::No_Activity_On_The_Radio,chan); chan->chanRelease(L3_HARDRELEASE_REQUEST,TermCause::Local(L3Cause::Radio_Interface_Failure)); // TODO: Is this right? Will the channel be immediately re-available? return; }