// (pat) This is started when SACCH is opened, and runs forever.
// The SACCHLogicalChannel are created by the SDCCHLogicalChannel and TCHFACCHLogicalChannel constructors.
void SACCHLogicalChannel::serviceLoop()
{

	// run the loop
	unsigned count = 0;
	while (true) {

		// Throttle back if not active.
		if (!active()) {
			//OBJLOG(DEBUG) << "SACCH sleeping";
			// pat 5-2013: Vastly reducing the delays here and in L2LAPDm to try to reduce
			// random failures of handover and channel reassignment from SDCCH to TCHF.
			// Update: The further this sleep is reduced, the more reliable handover becomes.
			// I left it at 4 for a while but handover still failed sometimes.
			//sleepFrames(51);
#define USE_SEMAPHORE 0	// This does not work well - there appear to be hundreds of interrupts per second.
#if USE_SEMAPHORE
			// (pat) Update: Getting rid of the sleep entirely.  We will use a semaphore instead.
			// Note that the semaphore call may return on signal, which is ok here.
			cout << descriptiveString() << " WAIT" <<endl;
			sem_wait(&mOpenSignal);
			cout << descriptiveString() << " AFTER" <<endl;
#else
			sleepFrames(2);
#endif
			// A clever way to avoid the sleep above would be to wait for ESTABLISH primitive.
			// (But which do you wait on - the tx or the rx queue?
			continue;	// paranoid, check again.
		}

		// Send any outbound messages.  If the tx queue is empty send alternating SI5/6.
		// (pat) FIXME: implement this!
		if (const L3Message *l3msg = mTxQueue.readNoBlock()) {
			SAPI_t sapi = SAPI0;		// Determine sapi from PD.  This is probably unnecessary, they are probably all SAPI=3
			switch (l3msg->PD()) {
			case L3RadioResourcePD: sapi = SAPI0; break;
			case L3SMSPD: sapi = SAPI3; break;
			default:
				OBJLOG(ERR)<<"In SACCHLogicalChannel, unexpected"<<LOGVAR(l3msg->PD());
				break;
			}
			L2LogicalChannel::l2sendm(*l3msg,GSM::DATA,sapi);
			delete l3msg;
		} else {
			// Send alternating SI5/SI6.
			// These L3Frames were created with the UNIT_DATA primivitive.
			OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
			if (count%2) L2LogicalChannel::l2sendf(gBTS.SI5Frame());
			else L2LogicalChannel::l2sendf(gBTS.SI6Frame());
			count++;
		}

		// Receive inbound messages.
		// This read loop flushes stray reports quickly.
		while (true) {

			OBJLOG(DEBUG) << "polling SACCH for inbound messages";
			bool nothing = true;

			// Process SAP0 -- RR Measurement reports
			if (L3Frame *rrFrame = L2LogicalChannel::l2recv(0,0)) {
				nothing=false;
				bool isMeasurementReport = rrFrame->isData()
					&& rrFrame->PD() == L3RadioResourcePD && rrFrame->MTI() == L3RRMessage::MeasurementReport;
				if (isMeasurementReport) {
					// Neither of these 'ifs' should fail, but be safe.
					if (const L3Message* rrMessage = parseSACCHMessage(rrFrame)) {
						if (const L3MeasurementReport* measurement = dynamic_cast<typeof(measurement)>(rrMessage)) {
							OBJLOG(DEBUG) << "SACCH measurement report " << mMeasurementResults;
							mMeasurementResults = measurement->results();
							if (mMeasurementResults.MEAS_VALID() == 0) {
								addSelfRxLev(mMeasurementResults.RXLEV_SUB_SERVING_CELL_dBm());
							}
							// Add the measurement results to the table
							// Note that the typeAndOffset of a SACCH match the host channel.
							gPhysStatus.setPhysical(this, mMeasurementResults);
							// Check for handover requirement.
							// (pat) TODO: This may block while waiting for a reply from a Peer BTS.
							Control::HandoverDetermination(mMeasurementResults,mAverageRXLEV_SUB_SERVICING_CELL,this);
						}
						delete rrMessage;
					}
					delete rrFrame;
				} else {
					// Send it off to Layer 3.  Who knows what might show up here.
					hostChan()->chanEnqueueFrame(rrFrame);
				}
			}

#if 0
			L3Message* rrMessage = parseSACCHMessage(rrFrame);
			delete rrFrame;
			if (rrMessage) {
				L3MeasurementReport* measurement = dynamic_cast<L3MeasurementReport*>(rrMessage);
				if (measurement) {
					mMeasurementResults = measurement->results();
					OBJLOG(DEBUG) << "SACCH measurement report " << mMeasurementResults;
					// Add the measurement results to the table
					// Note that the typeAndOffset of a SACCH match the host channel.
					gPhysStatus.setPhysical(this, mMeasurementResults);
					// Check for handover requirement.
					// (pat) TODO: This may block while waiting for a reply from a Peer BTS.
					Control::HandoverDetermination(mMeasurementResults,this);
					delete rrMessage;
				} else {
					if (Control::l3rewrite()) {
						OBJLOG(DEBUG) << "chanEnqueuel3msg:"<<rrMessage;
						hostChan()->chanEnqueuel3msg(rrMessage);
					} else {
						OBJLOG(NOTICE) << "SACCH SAP0 sent unaticipated message " << rrMessage;
						delete rrMessage;
					}
				}
			}
#endif

			// Process SAP3 -- SMS
			L3Frame *smsFrame = L2LogicalChannel::l2recv(0,3);
			if (smsFrame) {
				nothing=false;

				OBJLOG(DEBUG) <<"received SMS frame:"<<smsFrame;

				// The SACCH messages are polled from by the single L3LogicalChannel thread that handles this MS.
				//if (smsFrame) { Control::gCSL3StateMachine.csl3Write(new Control::GenericL3Msg(smsFrame,this)); }
				//L3Message *smsMessage = parseSACCHMessage(smsFrame);
				//OBJLOG(DEBUG) <<"parsed SMS message:"<<smsMessage;
				//delete smsFrame;
				hostChan()->chanEnqueueFrame(smsFrame);
			}

			// Did we get anything from the phone?
			// If not, we may have lost contact.  Bump the RSSI to induce more power
			if (nothing) RSSIBumpDown(gConfig.getNum("Control.SACCHTimeout.BumpDown"));

			// Nothing happened?
			if (nothing) break;
		}
	}
}
void SACCHLogicalChannel::serviceLoop()
{
	// run the loop
	unsigned count = 0;
	while (true) {

		// Throttle back if not active.
		if (!active()) {
			OBJLOG(DEBUG) << "SACCH sleeping";
			sleepFrames(51);
			continue;
		}

		// TODO SMS -- Check to see if the tx queues are empty.  If so, send SI5/6,
		// otherwise sleep and continue;

		// Send alternating SI5/SI6.
		OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
		if (count%2) LogicalChannel::send(gBTS.SI5Frame());
		else LogicalChannel::send(gBTS.SI6Frame());
		count++;

		// Receive inbound messages.
		// This read loop flushes stray reports quickly.
		while (true) {

			OBJLOG(DEBUG) << "polling SACCH for inbound messages";
			bool nothing = true;

			// Process SAP0 -- RR Measurement reports
			L3Frame *rrFrame = LogicalChannel::recv(0,0);
			if (rrFrame) nothing=false;
			L3Message* rrMessage = processSACCHMessage(rrFrame);
			delete rrFrame;
			if (rrMessage) {
				L3MeasurementReport* measurement = dynamic_cast<L3MeasurementReport*>(rrMessage);
				if (measurement) {
					mMeasurementResults = measurement->results();
					OBJLOG(DEBUG) << "SACCH measurement report " << mMeasurementResults;
					// Add the measurement results to the table
					// Note that the typeAndOffset of a SACCH match the host channel.
					gPhysStatus.setPhysical(this, mMeasurementResults);
				} else {
					OBJLOG(NOTICE) << "SACCH SAP0 sent unaticipated message " << rrMessage;
				}
				delete rrMessage;
			}

			// Process SAP3 -- SMS
			L3Frame *smsFrame = LogicalChannel::recv(0,3);
			if (smsFrame) nothing=false;
			L3Message* smsMessage = processSACCHMessage(smsFrame);
			delete smsFrame;
			if (smsMessage) {
				const SMS::CPData* cpData = dynamic_cast<const SMS::CPData*>(smsMessage);
				if (cpData) {
					OBJLOG(INFO) << "SMS CPDU " << *cpData;
					Control::TransactionEntry *transaction = gTransactionTable.find(this);
					try {
						if (transaction) {
							Control::InCallMOSMSController(cpData,transaction,this);
						} else {
							OBJLOG(WARNING) << "in-call MOSMS CP-DATA with no corresponding transaction";
						}
					} catch (Control::ControlLayerException e) {
						//LogicalChannel::send(RELEASE,3);
						gTransactionTable.remove(e.transactionID());
					}
				} else {
					OBJLOG(NOTICE) << "SACCH SAP3 sent unaticipated message " << rrMessage;
				}
				delete smsMessage;
			}

			// Anything from the SIP side?
			// MTSMS (delivery from SIP to the MS)
			Control::TransactionEntry *sipTransaction = mTransactionFIFO.readNoBlock();
			if (sipTransaction) {
				OBJLOG(INFO) << "SIP-side transaction: " << sipTransaction;
				assert(sipTransaction->service() == L3CMServiceType::MobileTerminatedShortMessage);
				try {
					Control::MTSMSController(sipTransaction,this);
				} catch (Control::ControlLayerException e) {
					//LogicalChannel::send(RELEASE,3);
					gTransactionTable.remove(e.transactionID());
				}
			}

			// Nothing happened?
			if (nothing) break;
		}

	}
}
Example #3
0
void SACCHLogicalChannel::serviceLoop()
{

	// run the loop
	unsigned count = 0;
	while (true) {

		// Throttle back if not active.
		if (!active()) {
			//OBJLOG(DEBUG) << "SACCH sleeping";
			sleepFrames(51);
			continue;
		}

		// TODO SMS -- Check to see if the tx queues are empty.  If so, send SI5/6,
		// otherwise sleep and continue;

		// Send alternating SI5/SI6.
		// These L3Frames were created with the UNIT_DATA primivitive.
		OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
		// To be thread safe make a copy with the system information locked
		L3Frame frame;
		gBTS.infoLock().lock();
		if (count%2) {
			gBTS.regenerateSI5();
			frame = gBTS.SI5Frame();
		}
		else frame = gBTS.SI6Frame();
		gBTS.infoLock().unlock();
		LogicalChannel::send(frame);
		count++;

		// Receive inbound messages.
		// This read loop flushes stray reports quickly.
		while (true) {

			OBJLOG(DEBUG) << "polling SACCH for inbound messages";
			bool nothing = true;

			// Process SAP0 -- RR Measurement reports
			L3Frame *rrFrame = LogicalChannel::recv(0,0);
			if (rrFrame) nothing=false;
			L3Message* rrMessage = processSACCHMessage(rrFrame);
			delete rrFrame;
			if (rrMessage) {
				L3MeasurementReport* measurement = dynamic_cast<L3MeasurementReport*>(rrMessage);
				if (measurement) {
					mMeasurementResults = measurement->results();
					OBJLOG(DEBUG) << "SACCH measurement report " << mMeasurementResults;
					// Add the measurement results to the table
					// Note that the typeAndOffset of a SACCH match the host channel.
					gPhysStatus.setPhysical(this, mMeasurementResults);
					// Check for handover requirement.
					if (mMeasurementHoldOff.passed()) {
						mMeasurementHoldOff.future(gConfig.getNum("GSM.Handover.RepeatHoldoff"));
						Control::HandoverDetermination(mMeasurementResults,this);
					}
				} else {
					OBJLOG(NOTICE) << "SACCH SAP0 sent unanticipated message " << rrMessage;
				}
				delete rrMessage;
			}

			// Process SAP3 -- SMS
			L3Frame *smsFrame = LogicalChannel::recv(0,3);
			if (smsFrame) {
				nothing=false;
				switch (smsFrame->primitive()) {
					case ESTABLISH:
						{
							int id = gConnMap.find(this);
							if (id >= 0)
								gSigConn.send(Connection::SigEstablishSAPI,0x83,id);
							else
								LOG(ERR) << "SAP3 established on unmapped channel " << *this;
						}
						break;
					case DATA:
					case UNIT_DATA:
						{
							int id = gConnMap.find(this);
							if (id >= 0)
								gSigConn.send(0x83,id,smsFrame);
							else
								LOG(ERR) << "SAP3 data on unmapped channel " << *this;
						}
						break;
					default:
						LOG(INFO) << "non-data primitive " << smsFrame->primitive();
				}
				delete smsFrame;
			}

			// Did we get anything from the phone?
			// If not, we may have lost contact.  Bump the RSSI to induce more power
			//if (nothing) RSSIBumpDown(gConfig.getNum("Control.SACCHTimeout.BumpDown"));

			// Nothing happened?
			if (nothing) break;
		}

	}
}