Exemple #1
0
// 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);
}
Exemple #2
0
// 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;
}
Exemple #3
0
static void abortInboundHandover(RefCntPointer<TranEntry> transaction, RRCause cause, L3LogicalChannel *LCH=NULL)
{
    LOG(DEBUG) << "aborting inbound handover " << *transaction;
    unsigned holdoff = gConfig.getNum("GSM.Handover.FailureHoldoff");
    gPeerInterface.sendHandoverFailure(transaction->getHandoverEntry(true),cause,holdoff);
}
Exemple #4
0
// Warning: This runs in a separate thread.
void HandoverDetermination(const L3MeasurementResults* measurements, SACCHLogicalChannel* sacch)
{
    //LOG(DEBUG) <<measurements->text();
    // This is called from the SACCH service loop.
    if (! sacch->neighborAddMeasurements(sacch,measurements)) return;

    // (pat) TODO: If you add your own IP address to the sql neighbor list, the MS will return info on yourself,
    // which will attempt a handover to yourself unless you throw those measurement reports away here.
    // We should detect this and throw them out.
    // Currently processNeighborParams() detects this condition when it gets a Peer report (but not at startup!)
    // but we dont save the BSIC in memory so we dont have that information here where we need it.

    BestNeighbor bestn = HandoverDecision(measurements, sacch);
    LOG(DEBUG) << bestn;
    if (! bestn.mValid) {
        // No handover for now.
        return;
    }

    string whatswrong;	// pass by reference to getAddress()
    string peerstr = gNeighborTable.getAddress(bestn.mARFCN, bestn.mBSIC,whatswrong);
    if (peerstr.empty()) {
        LOG(INFO) << "measurement for unknown neighbor"<<LOGVAR2("ARFCN",bestn.mARFCN)<<LOGVAR2("BSIC",bestn.mBSIC) <<" "<<whatswrong;
        return;
    }
    if (gNeighborTable.holdingOff(peerstr.c_str())) {
        LOG(NOTICE) << "skipping "<<bestn.mHandoverCause<< " handover to " << peerstr << " due to holdoff";
        return;
    }

    // Find the transaction record.

    const L3LogicalChannel *mainChanConst = dynamic_cast<typeof(mainChanConst)>(sacch->hostChan());
    L3LogicalChannel *mainChan = const_cast<typeof(mainChan)>(mainChanConst);	// idiotic language
    // The RefCntPointer prevents the tran from being deleted while we are working here, as unlikely as that would be.
    const RefCntPointer<TranEntry> tran = mainChan->chanGetVoiceTran();
    if (tran == NULL) {
        LOG(ERR) << "active SACCH with no transaction record: " << *sacch;
        return;
    }
    if (tran->getGSMState() != CCState::Active) {
        LOG(DEBUG) << "skipping handover for transaction " << tran->tranID()
                   << " due to state " << tran->getGSMState();
        return;
    }
    // Don't hand over an emergency call based on an IMEI.  It WILL fail.
    if (tran->servicetype() == GSM::L3CMServiceType::EmergencyCall &&
            //Unconst(tran)->subscriber().mImsi.length() == 0)
            tran->subscriber().mImsi.length() == 0) {
        LOG(ALERT) << "cannot handover emergency call with non-IMSI subscriber ID: " << *tran;
        return;
    }

    // (pat) Dont handover a brand new transaction.  This also prevents an MS from bouncing
    // back and forth between two BTS.  We dont need a separate timer for this handover holdoff,
    // we can just use the age of the Transaction.
    // I dont see any such timer in the spec; I am reusing T3101ms, which is not correct but vaguely related.
    // Update - this is now unnecessary because the averaging method of myRxLevel prevents a handover for the first 5-10 secs.
    unsigned age = tran->stateAge();	// in msecs.
    unsigned holdoff = 1000 * gConfig.getNum("GSM.Timer.Handover.Holdoff"); // default 10 seconds.
    if (age < holdoff) {
        WATCH("skipping handover for transaction " << tran->tranID() << " due to young"<<LOGVAR(age));
        LOG(DEBUG) << "skipping handover for transaction " << tran->tranID() << " because age "<<age<<"<"<<holdoff;
        return;
    }
    LOG(INFO) << "preparing "<<bestn.mHandoverCause<<" handover of " << tran->tranID()
              << " to " << peerstr << " with downlink RXLEV=" << bestn.mRxlev << " dbm";

    // The handover reference will be generated by the other BTS.
    // We don't set the handover reference or state until we get RSP HANDOVER.

    // TODO: Check for handover request to our own BTS and avoid it.  Dont forget to check the port too.

    // Form and send the message.
    // This message is re-sent every 0.5s (the periodicity of measurement reports) until the peer answers.
    // pats TODO: we could surely do this a better way.
    gPeerInterface.sendHandoverRequest(peerstr,tran,bestn.mHandoverCause);
}