// (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; } } }
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; } } }