um.cc

00001 /*
00002  * Copyright (c) 2003 Ericsson Telecommunicatie B.V.
00003  * All rights reserved.
00004  * 
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *     notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *     notice, this list of conditions and the following disclaimer in the
00012  *     documentation and/or other materials provided with the
00013  *     distribution.
00014  * 3. Neither the name of Ericsson Telecommunicatie B.V. may be used
00015  *     to endorse or promote products derived from this software without
00016  *     specific prior written permission.
00017  * 
00018  * 
00019  * THIS SOFTWARE IS PROVIDED BY ERICSSON TELECOMMUNICATIE B.V. AND
00020  * CONTRIBUTORS "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
00021  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00022  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00023  * IN NO EVENT SHALL ERICSSON TELECOMMUNICATIE B.V., THE AUTHOR OR HIS
00024  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00026  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00027  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00028  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00030  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  * 
00032  * 
00033  * Contact for feedback on EURANE: eurane@ti-wmc.nl
00034  * EURANE = Enhanced UMTS Radio Access Network Extensions
00035  * website: http://www.ti-wmc.nl/eurane/
00036  */
00037 
00038 /*
00039  * $Id: um.cc,v 1.1 2005/04/27 14:30:10 simon Exp $
00040  */
00041 
00042 // UM is basically UM with all the support for multiple flows taken out. Also,
00043 // the credit allocation algorithm is replaced by a simple 'send-x-PDUs-per-TTI'
00044 // algorithm.
00045 
00046 #include <um.h>
00047 #include <mac.h>
00048 #include <flags.h>
00049 #include <random.h>
00050 #include <address.h>
00051 #include <iostream>
00052 
00053 
00054 static class UMRlcClass:public TclClass 
00055 {
00056 public:
00057   UMRlcClass():TclClass("UMTS/RLC/UM")   {  } 
00058   TclObject *create(int, const char *const *) {
00059     return (new UM());
00060   }
00061 } class_um;
00062 
00063 
00064 
00065 UM::UM():RLC(), temporaryPacket_(this), nextExpectedSDU_(0), nextExpectedSeqno_(0),
00066 nextExpectedSegment_(0), errorInSDU_(0), seqno_(0), send_status_(0),
00067 SDU_size_(0), maxseq_(-1), highest_ack_(0), maxseen_(0), next_(0),
00068 d_address_(0), macDA_(0), tti_timer_(this, RLC_TIMER_TTI)
00069 {
00070    bind("win_", &win_);
00071    bind_time("temp_pdu_timeout_time_", &tempPDUTimeOutTime_);
00072    bind("buffer_level_max_", &bufferLevelMax_);
00073    bind("payload_", &payloadSize_);
00074    bind_time("TTI_", &TTI_);
00075    bind("TTI_PDUs_", &TTI_PDUs_);
00076    bind("length_indicator_", &length_indicator_);
00077    bind("min_concat_data_", &min_concat_data_);
00078    bind("bandwidth_", &bandwidth_);
00079    bind("macDA_", &macDA_);
00080 
00081    // Calculate the size in bytes of Length Indicator including the
00082    // Extention bit. If a PDU consists of multiple parts, for each
00083    // part these two fields are added to the header. If the PDU consists
00084    // of only one part, these fields are omitted (9.2.1.4 + 9.2.2.5 from
00085    // 3GPP TS 25.322 v5.6.0).
00086    lengthIndicatorSize_ = ((length_indicator_ + 1) / 8);
00087    temporaryPacket_.p = NULL;
00088 
00089    tti_timer_.sched(TTI_ - 0.001);
00090 
00091 }
00092 
00093 
00094 int UM::command(int argc, const char *const *argv)
00095 {
00096    if (argc == 2) {
00097       Tcl & tcl = Tcl::instance();
00098       if (strcmp(argv[1], "TTI") == 0) {
00099          tcl.resultf("%f", TTI_);
00100          return TCL_OK;
00101       } else if (strcmp(argv[1], "BW") == 0) {
00102          tcl.resultf("%f", bandwidth_);
00103          return TCL_OK;
00104       } // else if (strcmp(argv[1], "start_TTI") == 0) {
00105 //          // do nothing (so ignore this methodcall, the timers are started by the
00106 //          // constructor)
00107 //          return TCL_OK;
00108 //       }
00109    } else if (argc == 3) {
00110       if (strcmp(argv[1], "macDA") == 0) {
00111          int nodeID = Address::instance().str2addr(argv[2]);
00112 
00113          macDA_ = nodeID;
00114          return (TCL_OK);
00115       } else if (strcmp(argv[1], "addr") == 0) {
00116          address_ = Address::instance().str2addr(argv[2]);
00117          return (TCL_OK);
00118       } else if (strcmp(argv[1], "daddr") == 0) {
00119          int nodeID = Address::instance().str2addr(argv[2]);
00120 
00121          d_address_ = nodeID;
00122          return (TCL_OK);
00123       }
00124    }
00125    int returnvalue = LL::command(argc, argv);
00126 
00127 
00128    return returnvalue;
00129 }
00130 
00131 void UM::recv(Packet * p, Handler * h)
00132 {
00133   if (debug_)
00134     std::cerr << "entering UM::recv()" << std::endl;
00135 
00136    hdr_cmn    *ch = HDR_CMN(p);
00137 
00138    // If the direction is up, we don't have to change anything, and the packet
00139    // can be processed.
00140    if (ch->direction() == hdr_cmn::UP) {
00141       hdr_rlc    *llh = hdr_rlc::access(p);
00142 
00143       if (debug_)
00144         std::cerr << NOW << " UM::recv() packet going UP" << std::endl;
00145 
00146       if (llh->dst() != address_ || ch->error() > 0) {
00147          // The packet was meant for another UE, or it contained errors. In
00148          // both cases it will be dropped.
00149         if (debug_)
00150           std::cerr << NOW << " UM::recv() dropping packet"
00151                     << " llh->dst()=" << llh->dst()
00152                     << " address_=" << address_  
00153                     << " ch->error()=" << ch->error()
00154                     << std::endl;
00155         Packet::free(p);
00156         return;
00157       }
00158       // Check the type op packets
00159       if (llh->lltype() == RLC_DATA) {
00160          // We only pass the last PDU of an SDU. The rest of the PDUs are not
00161          // needed for the reassembly and can be freed.
00162 
00163          hdr_tcp    *tcph = hdr_tcp::access(p);
00164          hdr_rlc    *llh = hdr_rlc::access(p);
00165          int temp_sdu = tcph->seqno();
00166          int temp_seqno = llh->seqno();
00167          int temp_segment = llh->segment();
00168 
00169 
00170          if (temp_seqno == llh->eopno()) {
00171             // The packet is the last PDU of an SDU. Now, check whether the
00172             // complete SDU has been received. If so, pass the SDU up. In either
00173             // case, the next expected packet will be the first PDU of the next
00174             // SDU.
00175             if (nextExpectedSeqno_ == temp_seqno && nextExpectedSDU_ == temp_sdu
00176                 && errorInSDU_ == 0) {
00177                makeSDU(p);
00178             } else {
00179                Packet::free(p);
00180             }
00181             errorInSDU_ = 0;
00182             nextExpectedSDU_ = temp_sdu + 1;
00183             nextExpectedSeqno_ = temp_seqno + 1;
00184          } else {
00185             // The packet is not the last PDU of an SDU. When the packet is the
00186             // expected packet, increase the next expected packet. If not, the
00187             // next packet is the next packet after this one. In that case,
00188             // set errorInSDU so that this current SDU will not be passed up,
00189             // unless this is the first segment of the SDU. In that case, the
00190             // missed PDU didn't belong to this SDU but to the previous one.
00191             if (nextExpectedSeqno_ == temp_seqno) {
00192                nextExpectedSeqno_++;
00193             } else {
00194                nextExpectedSDU_ = temp_sdu;
00195                nextExpectedSeqno_ = temp_seqno + 1;
00196                if (temp_segment != 0) {
00197                   errorInSDU_++;
00198                }
00199             }
00200             Packet::free(p);
00201          }
00202 
00203       } else {
00204         if (debug_)
00205           std::cerr << "UM::recv() dropping packet"
00206                     << " llh->type=" << llh->lltype()
00207                     << " ch->error()=" << ch->error()
00208                     << std::endl;
00209           
00210 
00211          // In case the type of the packet is something else, just drop it.
00212          drop(p);               // Unknown type, inclusing Positive Acknowledgements
00213       }
00214       return;
00215    } else {
00216       // We think that this should always be the case, so why set it again? For
00217       // testing purposes we should replace the following line by an assert
00218       // statement to check it.
00219       assert(ch->direction() == hdr_cmn::DOWN);
00220       sendDown(p);
00221    }
00222 
00223 }
00224 
00225 //----------------------------------------------------------------
00226 //enquePacket
00227 //
00228 //if possible (i.e. buffers are not full) enqueue packet in both
00229 // transmission and re-transmissionbuffer.
00230 // return 1 after succesfull enqueing
00231 // if not possible, return 0
00232 int UM::enquePacket(Packet * p)
00233 {
00234 
00235    int r_value = 0;
00236 
00237 
00238    r_value = enqueInBackOfTransmissionBuffer(p);
00239 
00240 
00241    return r_value;
00242 }
00243 
00244 int UM::enqueInBackOfTransmissionBuffer(Packet * p)
00245 {
00246 // return 1 when succesfully enqueued, 0 when buffer is full
00247 
00248    int r_value = 0;
00249 
00250 
00251    if (transmissionBuffer_.length() >= bufferLevelMax_) {
00252       drop(p);
00253    } else {
00254       transmissionBuffer_.enque(p);
00255       r_value = 1;
00256    }
00257 
00258 
00259    return r_value;
00260 }
00261 
00262 
00263 
00264 //---------------------------
00265 // sendDown processes a SDU into PDUs for transmission to MAC-HS
00266 // if possible, waiting temporary packets are filled up.
00267 //---------------------------
00268 
00269 void UM::sendDown(Packet * p)
00270 {
00271 
00272    // First check whether the SDU fits into the transmission buffer completely
00273 
00274    int numPDUs =
00275          (int) ceil((double) hdr_cmn::access(p)->size() /
00276                     (double) payloadSize_);
00277 
00278    if (numPDUs + transmissionBuffer_.length() >= bufferLevelMax_) {
00279       Packet::free(p);
00280       return;
00281    }
00282 
00283    int segment = 0;
00284 
00285    // First check whether there is still a not-finished PDU available that can
00286    // be used for concatenation.
00287    handleTemporaryPacket(p);
00288 
00289    if (hdr_rlc::access(p)->segment() == 0) {
00290       // There was something that could be concatenated, and thus the first
00291       // segment of the packet was already sent, so continue with the next.
00292       segment++;
00293    }
00294 
00295    while (p != NULL) {
00296       // Each loop the front of the SDU is removed and send in a PDU. This
00297       // results in a smaller and smaller SDU, until at some point in time
00298       // it is completely gone. In that case p equals to NULL and the
00299       // complete SDU is segmented.
00300 
00301       int concat_data = 0;
00302       bool close_PDU = false;
00303       int padding = 0;
00304       bool force_length_indicator = false;
00305       Packet     *c;
00306 
00307       int p_size = hdr_cmn::access(p)->size();
00308 
00309       close_PDU = p_size <= payloadSize_;
00310       force_length_indicator = p_size < payloadSize_;
00311 
00312       if (p_size <= (payloadSize_ - lengthIndicatorSize_)) {
00313          // decide whether the remaining data is too big too keep
00314          // in which case we send it on with padding
00315          concat_data = payloadSize_ - p_size - 2 * lengthIndicatorSize_;
00316          if (concat_data < min_concat_data_) {
00317             concat_data = 0;
00318             padding = payloadSize_ - p_size - lengthIndicatorSize_;
00319          } else {
00320             padding = 0;
00321          }
00322       }
00323 
00324       if (close_PDU) {
00325          // reuse packet p
00326          c = p;
00327          p = NULL;
00328       } else {
00329          // copy p's data into c and force concat_data and padding to 0
00330          concat_data = 0;
00331          padding = 0;
00332          c = p->copy();
00333       }
00334 
00335 
00336       //initialise shortcuts to c's header info
00337       hdr_cmn    *c_ch = HDR_CMN(c);
00338       hdr_rlc    *c_llh = hdr_rlc::access(c);
00339       hdr_cmn    *p_ch = NULL;
00340       hdr_ip     *p_iph = NULL;
00341 
00342       if (p != NULL) {
00343          p_ch = HDR_CMN(p);
00344          p_iph = hdr_ip::access(p);
00345       }
00346 
00347       c_llh->lptype() = c_ch->ptype();
00348       c_llh->lerror_ = c_ch->error();
00349       c_llh->lts_ = c_ch->timestamp();
00350       c_llh->segment() = segment;
00351       segment++;
00352 
00353       if (close_PDU) {
00354 
00355          if (SDU_size_ > 0) {
00356             c_llh->lsize_ = SDU_size_;
00357          } else {
00358             c_llh->lsize_ = c_ch->size();
00359          }
00360 
00361          SDU_size_ = -1;
00362 
00363       } else if (SDU_size_ < 0) {
00364          assert(p != NULL);
00365          SDU_size_ = hdr_cmn::access(p)->size();
00366       }
00367 
00368       c_llh->lltype() = RLC_DATA;
00369       c_llh->seqno() = seqno_;
00370       seqno_++;
00371       c_llh->dst() = d_address_;
00372       c_llh->src() = address_;
00373       c_llh->lengthInd_ = 0;
00374 
00375       // [Nicola] This is totally crazy. Don't you see that
00376       // c_llh->seqno_ has been set a few lines above???
00377 //       if (close_PDU) {
00378 //          // c_llh->eopno_ needs to be set to c_llh->seqno_. However, at this
00379 //          // moment the seqno is not known yet. So, set it to EOPNO_TO_SEQNO
00380 //          // and check when seqno is set whether eopno is EOPNO_TO_SEQNO. If this
00381 //          // is the case eopno can be replaced by seqno.
00382 //          c_llh->eopno_ = EOPNO_TO_SEQNO;
00383 //       } else {
00384 //          c_llh->eopno_ = EOPNO_NOT_EOP;
00385 //       }
00386 
00387       // [Nicola] This is much better:
00388       if (close_PDU) {        
00389         c_llh->eopno_ = c_llh->seqno();
00390       } else {
00391         c_llh->eopno_ = EOPNO_NOT_EOP;
00392       }
00393       if (concat_data || padding || force_length_indicator) {
00394          c_llh->lengthInd_++;
00395       }
00396 
00397       if (close_PDU) {
00398          c_llh->payload_[PAYLOAD_FLD1] = c_ch->size();
00399       } else {
00400         if (force_length_indicator) {
00401           c_llh->payload_[PAYLOAD_FLD1] = payloadSize_ - lengthIndicatorSize_;
00402         } else {
00403           c_llh->payload_[PAYLOAD_FLD1] = payloadSize_;
00404         }
00405       }
00406       c_llh->payload_[PAYLOAD_FLD2] = 0;
00407       c_llh->payload_[PAYLOAD_FLD3] = 0;
00408 
00409       c_llh->padding_ = padding; //padding will always have the correct value here
00410 
00411       // WTF? this is really ugly, bad things will happen here!
00412       char       *mh = (char *) c->access(hdr_mac::offset_);
00413       struct hdr_mac *c_dh = (struct hdr_mac *) mh;
00414 
00415       c_ch->ptype() = PT_UM;
00416       c_dh->macDA_ = macDA_;
00417       c_dh->macSA_ = -1;
00418       c_dh->hdr_type() = ETHERTYPE_RLC;
00419       c_ch->timestamp() = Scheduler::instance().clock();
00420       c_ch->direction() = hdr_cmn::DOWN;
00421 
00422       // Do we have space left over for concatenation?
00423       if (close_PDU) {
00424          if (concat_data) {
00425             // Store the current packet and do not send it down.
00426             // Because this is also the last part of the SDU we can
00427             // stop this method.
00428             c_llh->lengthInd_++;
00429 
00430             StoreTemporaryPacket(c, concat_data);
00431             break;              //exit while loop
00432          }
00433       } else {
00434          // reduce the size of the SDU by an amount of the
00435          // payload that will be sent in this PDU.
00436          p_ch->size() = p_ch->size() - c_llh->payload_[PAYLOAD_FLD1];
00437       }
00438 
00439       assert(c_ch->error() == 0);
00440 
00441       c_ch->size() = payloadSize_;
00442 
00443 
00444       //----------
00445       if (enquePacket(c) == 0) {
00446       } else {
00447       }
00448 
00449    }
00450 
00451 
00452 }
00453 
00454 // This method is called when a PDU is created that is not full, and just stores
00455 // the PDU into a vector that will be inspected
00456 
00457 void UM::StoreTemporaryPacket(Packet * p, int concat_data)
00458 {
00459 
00460    temporaryPacket_.p = p;
00461    temporaryPacket_.concat_data = concat_data;
00462    // We can't wait forever, because then we have a problem at the end of a
00463    // burst of traffic: then the last PDU won't be send, causing timeouts and
00464    // retransmissions. So, we do want to send the incomplete packet anyway
00465    // at some point in time.
00466    temporaryPacket_.tempPDUTimer.sched(tempPDUTimeOutTime_);
00467 }
00468 
00469 
00470 void UM::handleTemporaryPacket(Packet * p)
00471 {
00472 
00473 
00474    if (p == NULL) {
00475       return;
00476    }
00477    // Fill the temporary PDU with data from the new SDU, and delete that
00478    // part of the SDU. Then store the temporary PDU in the Transmission
00479    // and Retransmission Buffers and return the remaining part of the SDU.
00480 
00481    // Concatenation (3GPP TS 25.301 5.3.2.2): If the contents of an RLC
00482    // SDU cannot be carried by one RLC PDU, the first segment of the next
00483    // RLC SDU may be put into the RLC PDU in concatenation with the last
00484    // segment of the previous RLC SDU.
00485 
00486    if (temporaryPacket_.p != NULL) {
00487 
00488       if (hdr_cmn::access(p)->size() > payloadSize_) {
00489 
00490          // TODO: check whether temporaryPacket exists
00491 
00492          hdr_rlc::access(temporaryPacket_.p)->payload_[1] =
00493                temporaryPacket_.concat_data;
00494          // this variable was previously set in makePDU
00495          SDU_size_ = hdr_cmn::access(p)->size();
00496          hdr_cmn::access(p)->size() =
00497                hdr_cmn::access(p)->size() - temporaryPacket_.concat_data;
00498 
00499          // Set the segemnt to 0, in this way it is known that segmentation
00500          // did occur.
00501          hdr_rlc::access(p)->segment() = 0;
00502 
00503       } else {
00504 
00505          hdr_rlc::access(temporaryPacket_.p)->lengthInd_--;
00506          hdr_rlc::access(temporaryPacket_.p)->padding_ =
00507                temporaryPacket_.concat_data + lengthIndicatorSize_;
00508       }
00509       enquePacket(temporaryPacket_.p);
00510 
00511       // Now the temporary PDU has been sent, delete the timer
00512       // so that the timeout will not occur anymore.
00513       temporaryPacket_.tempPDUTimer.cancel();
00514       temporaryPacket_.p = NULL;
00515    }
00516 }
00517 
00518 
00519 void UM::timeout(int tno, int flowID)
00520 {
00521    vector < int >temp_vect;
00522    double TTI_time_;
00523 
00524    switch (tno) {
00525      case RLC_TIMER_TEMP_PDU:
00526 
00527         // Before we stored the temporary PDU in the
00528         // vector, we set the number of Length Indicators
00529         // and the number of bytes that could be
00530         // concatenated. Because we are not concatenating,
00531         // but padding we need one Length Indicator less,
00532         // thus we have to pad more.
00533         hdr_rlc::access(temporaryPacket_.p)->lengthInd_--;
00534         hdr_rlc::access(temporaryPacket_.p)->padding_ =
00535               temporaryPacket_.concat_data + lengthIndicatorSize_;
00536         enquePacket(temporaryPacket_.p);
00537 
00538         break;
00539      case RLC_TIMER_TTI:
00540         TTI_time_ = Scheduler::instance().clock();
00541 
00542         // TTI_PDUs_ now is fixed and determined via TCL
00543         //TTI_PDUs_ = (int) (bandwidth_ * TTI_) / (payloadSize_ * 8);
00544 
00545         tti_timer_.resched(TTI_);
00546         for (int i = 0; i < TTI_PDUs_; i++) {
00547            //send_packet_down
00548            Packet     *p = transmissionBuffer_.deque();
00549 
00550            if (p != NULL) {
00551              hdr_cmn *ch = HDR_CMN(p);
00552              hdr_rlc    *llh = hdr_rlc::access(p);
00553              if (debug_)
00554                {
00555 
00556                  std::cerr << "UM::timeout() sending packet"
00557                            << " seqno=" << llh->seqno()
00558                            << " eopno=" << llh->eopno()            
00559                            << std::endl;                
00560 
00561                }
00562              //assert(ch->direction() == hdr_cmn::DOWN);
00563              ch->direction() = hdr_cmn::DOWN;
00564              downtarget_->recv(p);
00565            }
00566         }
00567         break;
00568      default:
00569         break;
00570    }
00571 
00572 }
00573 
00574 void UM::completePDU(Packet * p)
00575 {
00576 
00577 
00578    hdr_rlc    *llh = hdr_rlc::access(p);
00579 
00580    // If the packet is transmitted for the first time, set the sequence number
00581    // and possibly the eop number.
00582    if (llh->seqno() == -1) {
00583       llh->seqno() = seqno_;
00584       seqno_++;
00585       if (llh->eopno() == EOPNO_TO_SEQNO) {
00586          llh->eopno() = llh->seqno();
00587       }
00588    }
00589    return;
00590 }
00591 
00592 void UM::makeSDU(Packet * p)
00593 {
00594    hdr_cmn    *ch;
00595    hdr_rlc    *llh;
00596 
00597    assert(p);
00598    ch = HDR_CMN(p);
00599    llh = hdr_rlc::access(p);
00600 
00601    ch->ptype() = llh->lptype();
00602    ch->error() = llh->lerror();
00603    ch->timestamp() = llh->lts();
00604    ch->size() = llh->lsize();
00605 
00606    assert(ch->direction() == hdr_cmn::UP);
00607 
00608    uptarget_ ? sendUp(p) : Packet::free(p);
00609 }
00610 
00611 int UM::buff_size()
00612 {
00613    return(transmissionBuffer_.size());
00614 }
00615 
00616 void UM::CSwitch(double bandwidth, double TTI)
00617 {
00618            bandwidth_ = bandwidth;
00619               TTI_ = TTI;
00620 }
00621 

Generated on Wed Nov 26 15:47:29 2008 for NS-MIRACLE library by  doxygen 1.5.2