void UDPSocket::open(unsigned short localPort) { // create mSocketFD = socket(AF_INET,SOCK_DGRAM,0); if (mSocketFD<0) { perror("socket() failed"); devassert(0); throw SocketError(); } // pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes. int on = 1; setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); // bind struct sockaddr_in address; size_t length = sizeof(address); bzero(&address,length); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(localPort); if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { char buf[100]; sprintf(buf,"bind(port %d) failed",localPort); perror(buf); devassert(0); throw SocketError(); } }
void UDDSocket::open(const char* localPath) { // create mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0); if (mSocketFD<0) { perror("socket() failed"); devassert(0); throw SocketError(); } // bind struct sockaddr_un address; size_t length = sizeof(address); bzero(&address,length); address.sun_family = AF_UNIX; strcpy(address.sun_path,localPath); unlink(localPath); if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { char buf[1100]; sprintf(buf,"bind(path %s) failed",localPath); perror(buf); devassert(0); throw SocketError(); } }
void SipTransaction::sendAuthFailMessage(int code, string rand, string gsmRejectCode) { devassert(mstTranId); // The tran id is 0 for unregister messages. If one of those gets this far it is a bug. if (! mstTranId) { return; } DialogChallengeMessage *dmsg = new DialogChallengeMessage(mstTranId, DialogState::dialogFail,code); dmsg->dmRand = rand; dmsg->dmRejectCause = atoi(gsmRejectCode.c_str()); devassert(mstTranId); // The tran id is 0 for unregister messages. If one of those gets this far it is a bug. if (mstTranId) { SipCallbacks::ttAddMessage(mstTranId,dmsg); } }
// For DCCH channels (FACCH, SACCH, SDCCH): // This function calls virtual L2DL::l2dlWriteHighSide(L3Frame) which maps // to L2LAPDm::l2dlWriteHighSide() which interprets the primitive, and then // sends traffic data through sendUFrameUI(L3Frame) which creates an L2Frame // and sends it through several irrelevant functions to L2LAPDm::writeL1 // which calls (SAPMux)mDownstream->SAPMux::writeHighSide(L2Frame), // which does nothing but call mL1->writeHighSide(L2Frame), which is a pass-through // except that the SapMux uses mDownStream which is copied from mL1, so there is a // chance to redirect it. But wouldn't that be an error? // Anyway, L1Encoder::writeHighSide is usually overridden. // For TCH, it goes to XCCHL1Encoder::writeHighSide() which processes // the L2Frame primitive, then sends traffic data to TCHFACCHL1Encoder::sendFrame(), // which just enqueues the frame - it does not block. // A thread runs GSM::TCHFACCHL1EncoderRoutine() which // calls TCHFACCHL1Encoder::dispatch() which is synchronized with the gBTS clock, // unsynchronized with the queue, because it must send data no matter what. // Eventually it encodes the data and // calls (ARFCNManager*)mDownStream->writeHighSideTx(), which writes to the socket. // // From here and below only SAPI0 and SAPI3 are used. void L2SAPMux::sapWriteFromL3(const L3Frame& frame) { LOG(DEBUG) <<LOGVAR(frame); SAPI_t sap = frame.getSAPI(); if (!(sap == SAPI0 || sap == SAPI3 || sap == SAPI0Sacch || sap == SAPI3Sacch)) { devassert(0); sap = SAPI0; // This is a bug fix to avoid a crash. } unsigned sapi = SAP2SAPI(sap); devassert(mL2[sapi]); if (!mL2[sapi]) { return; } // Not initialized yet? Should never happen. // On SACCH L3_UNIT_DATA frames block for 480ms. // L3_DATA frames could block for minutes. if (mL2[sapi]) mL2[sapi]->l2dlWriteHighSide(frame); }
void L2SAPMux::sapWriteFromL1(const L2Frame& frame) { OBJLOG(DEBUG) << frame; unsigned sap; // (pat) Add switch to validate upstream primitives. The upstream only generates a few primitives; // the rest are created in L2LAPDm. switch (frame.primitive()) { case L2_DATA: sap = frame.SAPI(); assert(sap == SAPI0 || sap == SAPI3); if (mL2[sap]) { mL2[sap]->l2dlWriteLowSide(frame); } else { LOG(WARNING) << "received DATA for unsupported"<<LOGVAR(sap); } return; case PH_CONNECT: // All this does is fully reset LAPDm; copy it out to every SAP. for (int s = 0; s <= 3; s++) { if (mL2[s]) mL2[s]->l2dlWriteLowSide(frame); // Note: the frame may have the wrong SAP in it, but LAPDm doesnt care. } return; default: // If you get this assertion, make SURE you know what will happen upstream to that primitive. devassert(0); return; // make g++ happy. } }
void PDCHL1Downlink::send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle) { if (!idle && gConfig.getBool("Control.GSMTAP.GPRS")) { // Send to GSMTAP. gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(), frame2GsmTapType(frame), false, // not SACCH false, // this is a downlink frame); // The data. } switch (encoding) { case ChannelCodingCS1: // Process the 184 bit (23 byte) frame, leave result in mI. //mchCS1Enc.encodeFrame41(frame,0); //transmit(gBSNNext,mchCS1Enc.mI,qCS1,0); mchEnc.encodeCS1(frame); transmit(gBSNNext,mchEnc.mI,qCS1,0); break; case ChannelCodingCS4: //std::cout << "WARNING: Using CS4\n"; // This did not help the 3105/3101 errors: //mchCS4Enc.initCS4(); // DEBUG TEST!! Didnt help. //mchCS4Enc.encodeCS4(frame); // Result left in mI[]. //transmit(gBSNNext,mchCS4Enc.mI,qCS4,0); mchEnc.encodeCS4(frame); // Result left in mI[]. transmit(gBSNNext,mchEnc.mI,qCS4,0); break; default: LOG(ERR) << "unrecognized GPRS channel coding " << (int)encoding; devassert(0); } }
void SipClientTrLayer::sctStart() { LOG(DEBUG); ScopedLock lock(mstLock); // Must add to the tu map before sending the message because the reply can be fast enough to cause a race. gSipInterface.tuMapAdd(this); devassert(mstState == stInitializing); stWrite(&mstOutRequest); // Send the initial message. mstState = stCallingOrTrying; }
void L2SAPMux::sapStart() { LOG(DEBUG) <<descriptiveString(); devassert(mL1); startl1(); for (int s=0; s<4; s++) { // (pat) This starts sending LAPDm idle frames. if (mL2[s]) mL2[s]->l2dlOpen(descriptiveString()); } }
void CBCHLogicalChannel::cbchOpen() { if (mL1) mL1->l1init(); // (pat) L1FEC::l1init() sapStart(); // startl1(); devassert(mSACCH); if (mSACCH) { mSACCH->sacchInit(); mSACCH->sapStart(); } }
void SipTransaction::sendAuthOKMessage(SipMessage *sipmsg) { devassert(mstTranId); // The tran id is 0 for unregister messages. If one of those gets this far it is a bug. if (! mstTranId) { return; } string imsi = sipmsg->msmTo.uriUsername(); // The To: and From: have the same value for a REGISTER message. // Validate the imsi: if (imsi.empty()) { LOG(ERR) << "can't find imsi to store kc"; sendAuthFailMessage(400,"",""); return; } if (0 == strncasecmp(imsi.c_str(),"imsi",4)) { // happiness } else if (0 == strncasecmp(imsi.c_str(),"tmsi",4)) { // TODO: In future the user name could be a TMSI. LOG(ERR) << "SIP REGISTER message with TMSI not supported, userid="<<imsi; sendAuthFailMessage(400,"",""); return; } else { LOG(ERR) << "SIP REGISTER message with invalid IMSI:"<<imsi<<" Message:"<<sipmsg->msmContent; sendAuthFailMessage(400,"",""); return; } DialogAuthMessage *dmsg = new DialogAuthMessage(mstTranId, DialogState::dialogActive,200); dmsg->dmPAssociatedUri = sipmsg->msmHeaders.paramFind("P-Associated-URI"); // case doesnt matter dmsg->dmPAssertedIdentity = sipmsg->msmHeaders.paramFind("P-Asserted-Identity"); string authinfo = sipmsg->msmHeaders.paramFind("Authentication-Info"); if (! authinfo.empty()) { SipParamList params; parseToParams(authinfo, params); dmsg->dmKc = params.paramFind("cnonce"); // This is the way SR passes the Kc. } else { // There will not be a cnonce if the Registrar does not know it. Dont worry about it. //LOG(INFO) << "No Authenticate-Info header in SIP REGISTER response:"<<sipmsg->msmContent; } if (dmsg->dmKc.empty()) { dmsg->dmKc = sipmsg->msmHeaders.paramFind("P-GSM-Kc"); // This is the way Yate passes the Kc. } WATCHF("CNONCE: imsi=%s Kc=%s\n",imsi.c_str(),dmsg->dmKc.c_str()); if (! dmsg->dmKc.empty()) { LOG(DEBUG) << "Storing Kc:"<<LOGVAR(imsi)<<LOGVAR(dmsg->dmKc.size()); // We dont put Kc itself in the log. //gTMSITable.putKc(imsi.c_str()+4, kc, pAssociatedUri, pAssertedIdentity); } else { LOG(NOTICE) << "No Kc in SIP REGISTER response:"<<sipmsg->msmContent; } //NewTransactionTable_ttAddMessage(mstTranId,dmsg); SipCallbacks::ttAddMessage(mstTranId,dmsg); }
// FIXME: It blocks until L2LAPDm::sendIdle returns. That does not need to block. // Other channels call it too, which is somewhat nonsensical; the l2open for other channels is empty. // There is no close in L2 - the L1 encoder/decoder are closed individually when L2 sends a RELEASE/HARDRELEASE primitive. void L2LogicalChannel::lcstart() { LOG(DEBUG) <<this; devassert(mSACCH); if (mSACCH) mSACCH->sapStart(); sapStart(); mL3Out.clear(); mT3101.set(T3101ms); mT3109.reset(gConfig.GSM.Timer.T3109); // redundant with init in lcinit but cant be too careful. mT3111.reset(T3111ms); // redundant with init in lcinit but cant be too careful. }
// This only gets called once. // Looks like it never gets called SVG // Outbound void SipClientTrLayer::TLWriteLowSideV(SipMessage *request) { LOG(DEBUG); ScopedLock lock(mstLock); // For an Outbound Transaction, there is only one request except for invite, which also sends an ACK. if (! mstOutRequest.msmReqMethod.empty()) { assert(request->isACK()); } mstOutRequest = *request; devassert(mstState == stInitializing); stWrite(request); mstState = stCallingOrTrying; }
unsigned short UDPSocket::port() const { struct sockaddr_in name; socklen_t nameSize = sizeof(name); int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize); if (retVal==-1) { devassert(0); throw SocketError(); } return ntohs(name.sin_port); }
BitVector *GprsDecoder::getResult() { switch (getCS()) { case ChannelCodingCS4: return &mD_CS4; case ChannelCodingCS1: return &mD; default: devassert(0); // Others not supported yet. return NULL; } }
int DatagramSocket::read(char* buffer) { socklen_t temp_len = sizeof(mSource); int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0, (struct sockaddr*)&mSource,&temp_len); if ((length==-1) && (errno!=EAGAIN)) { perror("DatagramSocket::read() failed"); devassert(0); throw SocketError(); } return length; }
// Send the idle frame at specified Transceiver::SET_FILLER_FRAME) // Do not call send1msgframe, because it would try to set MAC fields. void PDCHL1Downlink::sendIdleFrame(RLCBSN_t bsn) { RLCMsgPacketDownlinkDummyControlBlock *msg = new RLCMsgPacketDownlinkDummyControlBlock(); devassert(msg->mUSF == 0); BitVector tobits(RLCBlockSizeInBits[ChannelCodingCS1]); msg->write(tobits); delete msg; //mchCS1Enc.encodeFrame41(tobits,0); //transmit(bsn,mchCS1Enc.mI,qCS1,Transceiver::SET_FILLER_FRAME); mchEnc.encodeCS1(tobits); transmit(bsn,mchEnc.mI,qCS1,Transceiver::SET_FILLER_FRAME); }
// Send the specified BitVector at the specified block time. void PDCHL1Downlink::transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags) { parent()->debug_test(); // Format the bits into the bursts. // GSM 05.03 4.1.5, 05.02 5.2.3 // NO! Dont do a wait here. The MAC serviceloop does this for all channels. // waitToSend(); // Don't get too far ahead of the clock. ARFCNManager *radio = getRadio(); if (!radio) { // For some testing, we might not have a radio connected. // That's OK, as long as we know it. GLOG(INFO) << "XCCHL1Encoder with no radio, dumping frames"; return; } int fn = bsn.FN(); int tn = TN(); for (int qi=0,B=0; B<4; B++) { Time nextWriteTime(fn,tn | transceiverflags); mchBurst.time(nextWriteTime); // Copy in the "encrypted" bits, GSM 05.03 4.1.5, 05.02 5.2.3. //OBJLOG(DEBUG) << "transmit mI["<<B<<"]=" << mI[B]; mI[B].segment(0,57).copyToSegment(mchBurst,3); mI[B].segment(57,57).copyToSegment(mchBurst,88); mchBurst.Hl(qbits[qi++]); mchBurst.Hu(qbits[qi++]); // Send it to the radio. //OBJLOG(DEBUG) << "transmit mchBurst=" << mchBurst; if (gConfig.getBool("Control.GSMTAP.GPRS")) { // Send to GSMTAP. gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(), TDMA_PDCH, false, // not SACCH false, // this is a downlink mchBurst, GSMTAP_TYPE_UM_BURST); } #if FEC_DEBUG if (1) { // Try decoding the frame we just encoded to see if it is correct. devassert(mchBurst.size() == gSlotLen); RxBurst rxb(mchBurst); ChannelCodingType cc; decodeLowSide(rxb, B, debugDecoder, &cc); } #endif radio->writeHighSideTx(mchBurst,"GPRS"); fn++; // This cannot overflow because it is within an RLC block. } }
// We dont really need to remember the LogicalChannel, but maybe we'll need it in the future. void PDCHL1FEC::mchOpen(TCHFACCHLogicalChannel *wlogchan) { // setGPRS has two affects: getTCH will consider the channel in-use; // and bursts start being delivered to us. // Note that the getTCH function normally doesnt even pay attention to whether // the channel is 'open' or not; it calls recyclable() that checks timers // and reuses the chan if they have expired. mchLogChan = wlogchan; devassert(mchLogChan->inUseByGPRS()); mchOldFec = mchLogChan->debugGetL1(); //mchReady = true; // finally }
void SACCHLogicalChannel::writeToL1(const L2Frame& frame) { // The SAP may or may not be present, depending on the channel type. OBJLOG(DEBUG) << frame; switch (frame.primitive()) { case L2_DATA: mL1->writeHighSide(frame); break; default: OBJLOG(ERR) << "unhandled primitive " << frame.primitive() << " in L2->L1"; devassert(0); } }
void L3SupServRegisterMessage::writeBody( L3Frame &dest, size_t &wp ) const { mFacility.writeTLV(0x1c,dest,wp); // Facility IE is mandatory, but it is permitted to be empty. // The network to MS direction does not have a version indicator. devassert(haveVersionIndicator() == false); // However, we are going to write the message anyway in case of bugs // where messages are converted to L3Frame and back. if (haveVersionIndicator()) { mVersionIndicator.writeTLV(0x7f,dest,wp); //dest.writeField(wp,0x7F,8); // The SS Version indicator IEI. //dest.writeField(wp,1,8); // Extreme dopeyness - it is a one byte field with a length specified. //dest.writeField(wp,mVersionIndicator.mValue,8); } }
// 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); }
void GprsEncoder::encodeCS4(const BitVector &src) { //if (sFecDebug) GPRSLOG(1) <<"encodeCS4 src\n"<<src; src.copyToSegment(mD_CS4,0,53*8); //if (sFecDebug) GPRSLOG(1) <<"encodeCS4 mD_CS4\n"<<mD_CS4; // mC.zero(); // DEBUG TEST!! Did not help. mD_CS4.fillField(53*8,0,7); // zero out 7 spare bits. mD_CS4.LSB8MSB(); // Ignores the last incomplete byte of 7 zero bits. //if (sFecDebug) GPRSLOG(1) <<"mC before parity\n"<<mC; // Parity is computed on original D before doing the USF translation above. mBlockCoder_CS4.writeParityWord(mD_CS4,mP_CS4); // Note that usf has been moved to the first three bits by the byte swapping above, // so when we write the 12 bits of GPRSUSFEncoding for usf into mC, it will overwrite // the original 3 parity bits. int reverseUsf = mD_CS4.peekField(0,3); // mU overwrites the first 3 bits of mD within mC. mU_CS4.fillField(0,GPRS::GPRSUSFEncoding[reverseUsf],12); // Result is left in mC. devassert(mC.peekField(0,12) == (unsigned) GPRS::GPRSUSFEncoding[reverseUsf]); devassert(mC.peekField(433,7) == 0); // unused bits not modified. //if (sFecDebug) GPRSLOG(1) <<"mC before interleave\n"<<mC; interleave41(); // Interleaves mC into mI. }
int DatagramSocket::read(char* buffer, unsigned timeout) { fd_set fds; FD_ZERO(&fds); FD_SET(mSocketFD,&fds); struct timeval tv; tv.tv_sec = timeout/1000; tv.tv_usec = (timeout%1000)*1000; int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv); if (sel<0) { perror("DatagramSocket::read() select() failed"); devassert(0); throw SocketError(); } if (sel==0) return -1; if (FD_ISSET(mSocketFD,&fds)) return read(buffer); return -1; }
void SACCHLogicalChannel::l2sendf(const L3Frame& frame) { SAPI_t sap = frame.getSAPI(); devassert(SAPIsSacch(sap)); if (sap != SAPI3Sacch) { LOG(NOTICE) << "unexpected"<<LOGVAR(sap)<<" in "<<LOGVAR(frame); } LOG(INFO) <<channelDescription() <<LOGVAR(sap) <<LOGVAR(chtype()) <<" " <<frame; switch (frame.primitive()) { case L3_ESTABLISH_REQUEST: // Fall through. case L3_DATA: case L3_UNIT_DATA: case L3_RELEASE_REQUEST: case L3_HARDRELEASE_REQUEST: mL3In.write(new L3Frame(frame)); return; default: assert(0); } }
// (pat) For data channels this is called by getTCH or getSDCCH. // TODO: Go through all the getTCH/getSDCCH users and make sure they lcstart. void L2LogicalChannel::lcinit() { LOG(DEBUG) <<this; assert(mL1); if (mL1) mL1->l1init(); // (pat) L1FEC::l1init() devassert(mSACCH); if (mSACCH) mSACCH->sacchInit(); // We set T3101 now so the channel will become recyclable if the caller does nothing with it; // the caller has this long to call lcstart before the channel goes back to the recyclable pool. mT3101.set(T3101ms); // It will be started again in l2start. mT3109.reset(gConfig.GSM.Timer.T3109); mT3111.reset(T3111ms); //mTRecycle.reset(500); // (pat) Must set a dummy value. if (!mlcMessageLoopRunning) { mlcMessageLoopRunning=true; mlcMessageServiceThread.start2((void*(*)(void*))MessageServiceLoop,this,8000*sizeof(void*)); } if (!mlcControlLoopRunning) { mlcControlLoopRunning=true; mlcControlServiceThread.start2((void*(*)(void*))ControlServiceLoop,this,8000*sizeof(void*)); } }
// Return decoded frame if success and B == 3, otherwise NULL. static BitVector *decodeLowSide(const RxBurst &inBurst, int B, GprsDecoder &decoder, ChannelCodingType *ccPtr) { inBurst.data1().copyToSegment(decoder.mI[B],0); inBurst.data2().copyToSegment(decoder.mI[B],57); // Save the stealing bits: // TODO: Save these as floats and do a correlation to pick the encoding. decoder.qbits[2*B] = inBurst.Hl(); decoder.qbits[2*B+1] = inBurst.Hu(); if (B != 3) { return NULL; } decoder.deinterleave(); bool success; BitVector *result; switch ((*ccPtr = decoder.getCS())) { case ChannelCodingCS4: success = decoder.decodeCS4(); LOG(DEBUG) << "CS-4 success=" << success; result = &decoder.mD_CS4; break; case ChannelCodingCS1: success = decoder.decode(); LOG(DEBUG) << "CS-1 success=" << success; result = &decoder.mD; break; default: devassert(0); // Others not supported yet. return NULL; } if (success) { result->LSB8MSB(); return result; } return NULL; }
// Return true if we send a block on the downlink. bool PDCHL1Downlink::send1DataFrame( //SVGDBG RLCDownEngine *engdown, RLCDownlinkDataBlock *block, // block to send. int makeres, // 0 = no res, 1 = optional res, 2 = required res. MsgTransactionType mttype, // Type of reservation unsigned *pcounter) { //ScopedLock lock(testlock); TBF *tbf = engdown->getTBF(); if (! setMACFields(block,mchParent,tbf,makeres,mttype,pcounter)) { return false; } // The rest of the RLC header is already set, but we did not know the tfi // when we created the RLCDownlinkDataBlocks (because tbf not yet attached) // so set tfi now that we know. Update 8-2012: Above comment is stale because we // make the RLCDownlinkBlocks on the fly now. block->mTFI = tbf->mtTFI; // block->mPR = 1; // DEBUG test; made no diff. tbf->talkedDown(); BitVector tobits = block->getBitVector(); // tobits deallocated when this function exits. if (block->mChannelCoding == 0) { devassert(tobits.size() == 184); } if (GPRSDebug & 1) { RLCBlockReservation *res = mchParent->getReservation(gBSNNext); std::ostringstream sshdr; block->text(sshdr,false); //block->RLCDownlinkDataBlockHeader::text(sshdr); ByteVector content(tobits); GPRSLOG(1) << "send1DataFrame "<<parent()<<" "<<tbf<<LOGVAR(tbf->mtExpectedAckBSN) << " "<<sshdr.str() <<" "<<(res ? res->str() : "") << LOGVAR2("content",content); //<< " enc="<<tbf->mtChannelCoding <<" "<<os.str() << "\nbits:" <<bits.str(); //<<" " <<block->str() <<"\nbits:" <<tobits.hexstr(); } #if FEC_DEBUG BitVector copybits; copybits.clone(tobits); #endif send1Frame(tobits,block->mChannelCoding,0); #if FEC_DEBUG BitVector *result = debugDecoder.getResult(); devassert(result); devassert(copybits == tobits); if (result && !(*result == tobits)) { int diffbit = -1; char thing[500]; for (int i = 0; i < (int)result->size(); i++) { thing[i] = '-'; if (result->bit(i) != tobits.bit(i)) { if (diffbit == -1) diffbit = i; thing[i] = '0' + result->bit(i); } } thing[result->size()] = 0; GPRSLOG(1) <<"encoding error" <<LOGVAR2("cs",(int)debugDecoder.getCS()) <<LOGVAR(diffbit) <<LOGVAR2("in:size",tobits.size()) <<LOGVAR2("out:size",result->size()) <<"\n"<<tobits <<"\n"<<*result <<"\n"<<thing; } else { //GPRSLOG(1) <<"encoding ok" <<LOGVAR2("cs",(int)debugDecoder.getCS()); } #endif return true; }