From e2732e2f5981d8898364541926df219f7b285b92 Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Thu, 7 Aug 2014 17:32:01 +0200 Subject: [PATCH 1/5] tbf: Make snd_ul_ud() and assemble_forward_llc() methods of UL TBF They are only called for UL TBF. Ticket: SYS#389 Sponsored by: On-Waves ehf --- src/tbf.cpp | 4 ++-- src/tbf.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tbf.cpp b/src/tbf.cpp index 22b8ce0e..b5bb123d 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -728,7 +728,7 @@ struct msgb *gprs_rlcmac_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) * Store received block data in LLC message(s) and forward to SGSN * if complete. */ -int gprs_rlcmac_tbf::assemble_forward_llc(const gprs_rlc_data *_data) +int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data) { const uint8_t *data = _data->block; uint8_t len = _data->len; @@ -1749,7 +1749,7 @@ void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh) } /* Send Uplink unit-data to SGSN. */ -int gprs_rlcmac_tbf::snd_ul_ud() +int gprs_rlcmac_ul_tbf::snd_ul_ud() { uint8_t qos_profile[3]; struct msgb *llc_pdu; diff --git a/src/tbf.h b/src/tbf.h index 23f8a7b7..b287e778 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -118,12 +118,8 @@ struct gprs_rlcmac_tbf { /* TODO: add the gettimeofday as parameter */ struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx); - /* TODO: extract LLC class? */ - int assemble_forward_llc(const gprs_rlc_data *data); - struct msgb *create_dl_ass(uint32_t fn); struct msgb *create_ul_ass(uint32_t fn); - int snd_ul_ud(); uint8_t tsc() const; @@ -339,6 +335,10 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf { /* blocks were acked */ int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi); + /* TODO: extract LLC class? */ + int assemble_forward_llc(const gprs_rlc_data *data); + int snd_ul_ud(); + /* Please note that all variables here will be reset when changing * from WAIT RELEASE back to FLOW state (re-use of TBF). * All states that need reset must be in this struct, so this is why From cf706b07752958693eaa831992f883252742a668 Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Thu, 7 Aug 2014 17:35:22 +0200 Subject: [PATCH 2/5] tbf: Make llc_dequeue a method of DL TBF llc_dequeue is only used in DL TBF to send the data from the BSSGP to the MS. Ticket: SYS#389 Sponsored by: On-Waves ehf --- src/tbf.cpp | 2 +- src/tbf.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tbf.cpp b/src/tbf.cpp index b5bb123d..e5b8863c 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -687,7 +687,7 @@ int gprs_rlcmac_tbf::rlcmac_diag() return 0; } -struct msgb *gprs_rlcmac_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) +struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) { struct msgb *msg; struct timeval *tv, tv_now; diff --git a/src/tbf.h b/src/tbf.h index b287e778..22504e7e 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -115,9 +115,6 @@ struct gprs_rlcmac_tbf { bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const; void set_state(enum gprs_rlcmac_tbf_state new_state); - /* TODO: add the gettimeofday as parameter */ - struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx); - struct msgb *create_dl_ass(uint32_t fn); struct msgb *create_ul_ass(uint32_t fn); @@ -310,6 +307,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf { int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb); struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts); + /* TODO: add the gettimeofday as parameter */ + struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx); + /* Please note that all variables here will be reset when changing * from WAIT RELEASE back to FLOW state (re-use of TBF). * All states that need reset must be in this struct, so this is why From 418a4230e0539583803deb189c30cc68c341da4e Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Fri, 8 Aug 2014 11:21:04 +0200 Subject: [PATCH 3/5] tbf, gprs_rlcmac_meas: Move the DL bandwidth variables to the DL TBF The bandwidth calculation as well as loss report is only done for DL TBF so move everything related to that in there. Ticket: SYS#389 Sponsored by: On-Waves ehf --- src/gprs_rlcmac.h | 6 +++--- src/gprs_rlcmac_meas.cpp | 28 ++++++++++++++-------------- src/tbf.cpp | 10 +++++++--- src/tbf.h | 17 +++++++++-------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index 148250ae..6f8a7a4f 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -62,10 +62,10 @@ struct gprs_rlcmac_cs { uint8_t block_payload; }; -int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, +int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received, uint16_t lost); -int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf); +int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf); int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr); @@ -73,7 +73,7 @@ int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi); int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf); -int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets); +int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets); /* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */ enum gprs_rlcmac_block_type { diff --git a/src/gprs_rlcmac_meas.cpp b/src/gprs_rlcmac_meas.cpp index 30958fc1..5a2e38e9 100644 --- a/src/gprs_rlcmac_meas.cpp +++ b/src/gprs_rlcmac_meas.cpp @@ -112,10 +112,10 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf) */ /* Lost frames reported from RLCMAC layer */ -int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, +int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received, uint16_t lost) { - struct timeval now_tv, *loss_tv = &tbf->meas.dl_loss_tv; + struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv; uint32_t elapsed; uint16_t sum = received + lost; @@ -126,8 +126,8 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d " "Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum); - tbf->meas.dl_loss_received += received; - tbf->meas.dl_loss_lost += lost; + tbf->m_bw.dl_loss_received += received; + tbf->m_bw.dl_loss_lost += lost; gettimeofday(&now_tv, NULL); elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7) @@ -139,16 +139,16 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, /* reset lost values and timestamp */ memcpy(loss_tv, &now_tv, sizeof(struct timeval)); - tbf->meas.dl_loss_received = 0; - tbf->meas.dl_loss_lost = 0; + tbf->m_bw.dl_loss_received = 0; + tbf->m_bw.dl_loss_lost = 0; return 0; } /* Give Lost report */ -int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf) +int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf) { - uint16_t sum = tbf->meas.dl_loss_lost + tbf->meas.dl_loss_received; + uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received; /* No measurement values */ if (!sum) @@ -156,7 +156,7 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf) LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: " "%d%%\n", tbf->imsi(), tbf->tlli(), - tbf->meas.dl_loss_lost * 100 / sum); + tbf->m_bw.dl_loss_lost * 100 / sum); return 0; } @@ -166,12 +166,12 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf) * downlink bandwidth */ -int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets) +int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets) { - struct timeval now_tv, *bw_tv = &tbf->meas.dl_bw_tv; + struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv; uint32_t elapsed; - tbf->meas.dl_bw_octets += octets; + tbf->m_bw.dl_bw_octets += octets; gettimeofday(&now_tv, NULL); elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7) @@ -181,11 +181,11 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets) LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: " "%d KBits/s\n", tbf->imsi(), tbf->tlli(), - tbf->meas.dl_bw_octets / elapsed); + tbf->m_bw.dl_bw_octets / elapsed); /* reset bandwidth values timestamp */ memcpy(bw_tv, &now_tv, sizeof(struct timeval)); - tbf->meas.dl_bw_octets = 0; + tbf->m_bw.dl_bw_octets = 0; return 0; } diff --git a/src/tbf.cpp b/src/tbf.cpp index e5b8863c..647710c2 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -268,7 +268,10 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf) { /* Give final measurement report */ gprs_rlcmac_rssi_rep(tbf); - gprs_rlcmac_lost_rep(tbf); + if (tbf->direction == GPRS_RLCMAC_DL_TBF) { + gprs_rlcmac_dl_tbf *dl_tbf = static_cast(tbf); + gprs_rlcmac_lost_rep(dl_tbf); + } LOGP(DRLCMAC, LOGL_INFO, "%s free\n", tbf_name(tbf)); if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE) @@ -519,9 +522,7 @@ static int setup_tbf(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_bts *bts, } /* set timestamp */ - gettimeofday(&tbf->meas.dl_bw_tv, NULL); gettimeofday(&tbf->meas.rssi_tv, NULL); - gettimeofday(&tbf->meas.dl_loss_tv, NULL); tbf->m_llc.init(); return 0; @@ -591,6 +592,9 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, llist_add(&tbf->list.list, &bts->dl_tbfs); tbf->bts->tbf_dl_created(); + gettimeofday(&tbf->m_bw.dl_bw_tv, NULL); + gettimeofday(&tbf->m_bw.dl_loss_tv, NULL); + return tbf; } diff --git a/src/tbf.h b/src/tbf.h index 22504e7e..54781c0f 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -179,17 +179,9 @@ struct gprs_rlcmac_tbf { unsigned int num_fT_exp; /* number of consecutive fT expirations */ struct { - struct timeval dl_bw_tv; /* timestamp for dl bw calculation */ - uint32_t dl_bw_octets; /* number of octets since bw_tv */ - struct timeval rssi_tv; /* timestamp for rssi calculation */ int32_t rssi_sum; /* sum of rssi values */ int rssi_num; /* number of rssi values added since rssi_tv */ - - struct timeval dl_loss_tv; /* timestamp for loss calculation */ - uint16_t dl_loss_lost; /* sum of lost packets */ - uint16_t dl_loss_received; /* sum of received packets */ - } meas; uint8_t cs; /* current coding scheme */ @@ -319,6 +311,15 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf { int32_t m_tx_counter; /* count all transmitted blocks */ uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */ + struct { + struct timeval dl_bw_tv; /* timestamp for dl bw calculation */ + uint32_t dl_bw_octets; /* number of octets since bw_tv */ + + struct timeval dl_loss_tv; /* timestamp for loss calculation */ + uint16_t dl_loss_lost; /* sum of lost packets */ + uint16_t dl_loss_received; /* sum of received packets */ + } m_bw; + protected: struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts); struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts, From eb100244b05ea2b732cefb50fd392f1eb7edfcd4 Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Fri, 8 Aug 2014 11:43:53 +0200 Subject: [PATCH 4/5] tbf, bts: Use tbf set_state method instead of tbf_new_state function All the function did was add debug output and call the set_state method. Move the debugging into the method and remove the function. Ticket: SYS#389 Sponsored by: On-Waves ehf --- src/bts.cpp | 10 +++++----- src/tbf.cpp | 33 ++++++++++++--------------------- src/tbf.h | 12 +++++++----- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/bts.cpp b/src/bts.cpp index 73344a38..86659621 100644 --- a/src/bts.cpp +++ b/src/bts.cpp @@ -467,7 +467,7 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta) return -EBUSY; } tbf->ta = qta >> 2; - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + tbf->set_state(GPRS_RLCMAC_FLOW); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); tbf_timer_start(tbf, 3169, m_bts.t3169, 0); LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n", @@ -515,7 +515,7 @@ void BTS::trigger_dl_ass( dl_tbf->ta = old_tbf->ta; dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE); /* change state */ - tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN); + dl_tbf->set_state(GPRS_RLCMAC_ASSIGN); dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); /* start timer */ tbf_timer_start(dl_tbf, 0, Tassign_pacch); @@ -527,7 +527,7 @@ void BTS::trigger_dl_ass( } dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE); /* change state */ - tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN); + dl_tbf->set_state(GPRS_RLCMAC_ASSIGN); dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); dl_tbf->assign_imsi(imsi); /* send immediate assignment */ @@ -774,7 +774,7 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, "TBF is gone TLLI=0x%08x\n", tlli); return; } - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + tbf->set_state(GPRS_RLCMAC_FLOW); /* stop pending assignment timer */ tbf->stop_timer(); if ((tbf->state_flags & @@ -800,7 +800,7 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, "TBF is gone TLLI=0x%08x\n", tlli); return; } - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + tbf->set_state(GPRS_RLCMAC_FLOW); if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { tbf->state_flags &= diff --git a/src/tbf.cpp b/src/tbf.cpp index 647710c2..98f66405 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -242,7 +242,7 @@ gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, tbf->m_tlli_valid = 1; /* no contention resolution */ tbf->m_contention_resolution_done = 1; tbf->ta = ta; /* use current TA */ - tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); + tbf->set_state(GPRS_RLCMAC_ASSIGN); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); tbf_timer_start(tbf, 3169, bts->t3169, 0); @@ -338,7 +338,7 @@ int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf) return 0; } -static const char *tbf_state_name[] = { +const char *gprs_rlcmac_tbf::tbf_state_name[] = { "NULL", "ASSIGN", "FLOW", @@ -347,15 +347,6 @@ static const char *tbf_state_name[] = { "RELEASING", }; -void tbf_new_state(struct gprs_rlcmac_tbf *tbf, - enum gprs_rlcmac_tbf_state state) -{ - LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n", - tbf_name(tbf), - tbf_state_name[tbf->state], tbf_state_name[state]); - tbf->set_state(state); -} - void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds, unsigned int microseconds) { @@ -412,7 +403,7 @@ void gprs_rlcmac_tbf::poll_timeout() if (ul_tbf->m_n3103 == ul_tbf->bts->bts_data()->n3103) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3103 exceeded\n"); - tbf_new_state(ul_tbf, GPRS_RLCMAC_RELEASING); + ul_tbf->set_state(GPRS_RLCMAC_RELEASING); tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0); return; } @@ -431,7 +422,7 @@ void gprs_rlcmac_tbf::poll_timeout() n3105++; if (n3105 == bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - tbf_new_state(this, GPRS_RLCMAC_RELEASING); + set_state(GPRS_RLCMAC_RELEASING); tbf_timer_start(this, 3195, bts_data()->t3195, 0); return; } @@ -449,7 +440,7 @@ void gprs_rlcmac_tbf::poll_timeout() n3105++; if (n3105 == bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - tbf_new_state(this, GPRS_RLCMAC_RELEASING); + set_state(GPRS_RLCMAC_RELEASING); tbf_timer_start(this, 3195, bts_data()->t3195, 0); return; } @@ -467,7 +458,7 @@ void gprs_rlcmac_tbf::poll_timeout() dl_tbf->n3105++; if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - tbf_new_state(dl_tbf, GPRS_RLCMAC_RELEASING); + dl_tbf->set_state(GPRS_RLCMAC_RELEASING); tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0); return; } @@ -632,7 +623,7 @@ void gprs_rlcmac_tbf::handle_timeout() if (!dl_tbf->upgrade_to_multislot) { /* change state to FLOW, so scheduler * will start transmission */ - tbf_new_state(dl_tbf, GPRS_RLCMAC_FLOW); + dl_tbf->set_state(GPRS_RLCMAC_FLOW); break; } @@ -1037,7 +1028,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t m_llc.reset(); /* final block */ rh->fbi = 1; /* we indicate final block */ - tbf_new_state(this, GPRS_RLCMAC_FINISHED); + set_state(GPRS_RLCMAC_FINISHED); /* return data block as message */ break; } @@ -1110,7 +1101,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t rh->fbi = 1; /* we indicate final block */ first_fin_ack = true; /* + 1 indicates: first final ack */ - tbf_new_state(this, GPRS_RLCMAC_FINISHED); + set_state(GPRS_RLCMAC_FINISHED); break; } /* we have no space left */ @@ -1284,7 +1275,7 @@ struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn) dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK; } else { dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; - tbf_new_state(new_dl_tbf, GPRS_RLCMAC_FLOW); + new_dl_tbf->set_state(GPRS_RLCMAC_FLOW); tbf_assign_control_ts(new_dl_tbf); /* stop pending assignment timer */ new_dl_tbf->stop_timer(); @@ -1476,7 +1467,7 @@ int gprs_rlcmac_dl_tbf::maybe_start_new_window() /* report all outstanding packets as received */ gprs_rlcmac_received_lost(this, received, 0); - tbf_new_state(this, GPRS_RLCMAC_WAIT_RELEASE); + set_state(GPRS_RLCMAC_WAIT_RELEASE); /* check for LLC PDU in the LLC Queue */ msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); @@ -1707,7 +1698,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t if (last_rh->cv == 0) { LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " "TBF\n"); - tbf_new_state(this, GPRS_RLCMAC_FINISHED); + set_state(GPRS_RLCMAC_FINISHED); /* Reset N3103 counter. */ this->m_n3103 = 0; } diff --git a/src/tbf.h b/src/tbf.h index 54781c0f..71721635 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -23,6 +23,7 @@ #include "gprs_rlcmac.h" #include "llc.h" #include "rlc.h" +#include #include @@ -217,6 +218,7 @@ protected: int extract_tlli(const uint8_t *data, const size_t len); + static const char *tbf_state_name[6]; }; @@ -238,9 +240,6 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf); int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf); -void tbf_new_state(struct gprs_rlcmac_tbf *tbf, - enum gprs_rlcmac_tbf_state state); - void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds, unsigned int microseconds); @@ -254,8 +253,13 @@ inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const return state != rhs; } +const char *tbf_name(gprs_rlcmac_tbf *tbf); + inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state) { + LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n", + tbf_name(this), + tbf_state_name[state], tbf_state_name[new_state]); state = new_state; } @@ -279,8 +283,6 @@ inline const char *gprs_rlcmac_tbf::imsi() const return m_imsi; } -const char *tbf_name(gprs_rlcmac_tbf *tbf); - inline time_t gprs_rlcmac_tbf::created_ts() const { return m_created_ts; From ca102af92b257aef0c745a8a422e3b50113c378b Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Fri, 8 Aug 2014 12:14:12 +0200 Subject: [PATCH 5/5] tbf: Split out UL/DL TBF methods into separate files tbf_ul.cpp for UL TBF methods tbf_dl.cpp for DL TBF methods This commit contains no code changes. Ticket: SYS#389 Sponsored by: On-Waves ehf --- src/Makefile.am | 2 + src/tbf.cpp | 1012 +---------------------------------------------- src/tbf_dl.cpp | 671 +++++++++++++++++++++++++++++++ src/tbf_ul.cpp | 418 ++++++++++++++++++++ 4 files changed, 1092 insertions(+), 1011 deletions(-) create mode 100644 src/tbf_dl.cpp create mode 100644 src/tbf_ul.cpp diff --git a/src/Makefile.am b/src/Makefile.am index beee4c09..d1ed701d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,6 +42,8 @@ libgprs_la_SOURCES = \ pcu_l1_if.cpp \ pcu_vty.c \ tbf.cpp \ + tbf_ul.cpp \ + tbf_dl.cpp \ bts.cpp \ poll_controller.cpp \ encoding.cpp \ diff --git a/src/tbf.cpp b/src/tbf.cpp index 98f66405..fd25e8df 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -36,185 +36,21 @@ extern "C" { #include #include -/* After sending these frames, we poll for ack/nack. */ -#define POLL_ACK_AFTER_FRAMES 20 -/* After receiving these frames, we send ack/nack. */ -#define SEND_ACK_AFTER_FRAMES 20 - - -static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = { -/* frame length data block max payload */ - { 0, 0, 0 }, - { 23, 23, 20 }, /* CS-1 */ - { 34, 33, 30 }, /* CS-2 */ - { 40, 39, 36 }, /* CS-3 */ - { 54, 53, 50 }, /* CS-4 */ -}; - -extern "C" { -int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, - uint8_t num_frames, uint32_t num_octets); -} - extern void *tall_pcu_ctx; static void tbf_timer_cb(void *_tbf); -inline gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const +gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const { return bts->bts_data(); } -static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf, - const uint8_t ms_class) -{ - if (!tbf->ms_class && ms_class) - tbf->ms_class = ms_class; -} - void gprs_rlcmac_tbf::assign_imsi(const char *imsi) { strncpy(m_imsi, imsi, sizeof(m_imsi)); m_imsi[sizeof(m_imsi) - 1] = '\0'; } -static struct gprs_rlcmac_dl_tbf *tbf_lookup_dl(BTS *bts, - const uint32_t tlli, const char *imsi) -{ - /* TODO: look up by IMSI first, then tlli, then old_tlli */ - return bts->dl_tbf_by_tlli(tlli); -} - -int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class, - const uint16_t pdu_delay_csec, - const uint8_t *data, const uint16_t len) -{ - LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this)); - if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) { - LOGP(DRLCMAC, LOGL_DEBUG, - "%s in WAIT RELEASE state " - "(T3193), so reuse TBF\n", tbf_name(this)); - tbf_update_ms_class(this, ms_class); - reuse_tbf(data, len); - } else { - /* the TBF exists, so we must write it in the queue - * we prepend lifetime in front of PDU */ - struct timeval *tv; - struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2, - "llc_pdu_queue"); - if (!llc_msg) - return -ENOMEM; - tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); - gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv); - tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); - gettimeofday(tv, NULL); - memcpy(msgb_put(llc_msg, len), data, len); - m_llc.enqueue(llc_msg); - tbf_update_ms_class(this, ms_class); - } - - return 0; -} - -static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, - const char *imsi, - const uint32_t tlli, const uint8_t ms_class, - const uint8_t *data, const uint16_t len) -{ - uint8_t trx, ta, ss; - int8_t use_trx; - struct gprs_rlcmac_ul_tbf *ul_tbf, *old_ul_tbf; - struct gprs_rlcmac_dl_tbf *dl_tbf; - int8_t tfi; /* must be signed */ - int rc; - - /* check for uplink data, so we copy our informations */ -#warning "Do the same look up for IMSI, TLLI and OLD_TLLI" -#warning "Refactor the below lines... into a new method" - ul_tbf = bts->bts->ul_tbf_by_tlli(tlli); - if (ul_tbf && ul_tbf->m_contention_resolution_done - && !ul_tbf->m_final_ack_sent) { - use_trx = ul_tbf->trx->trx_no; - ta = ul_tbf->ta; - ss = 0; - old_ul_tbf = ul_tbf; - } else { - use_trx = -1; - /* we already have an uplink TBF, so we use that TA */ - if (ul_tbf) - ta = ul_tbf->ta; - else { - /* recall TA */ - rc = bts->bts->timing_advance()->recall(tlli); - if (rc < 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown" - ", assuming 0\n"); - ta = 0; - } else - ta = rc; - } - ss = 1; /* PCH assignment only allows one timeslot */ - old_ul_tbf = NULL; - } - - // Create new TBF (any TRX) -#warning "Copy and paste with alloc_ul_tbf" - tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx); - if (tfi < 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); - /* FIXME: send reject */ - return -EBUSY; - } - /* set number of downlink slots according to multislot class */ - dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf, tfi, trx, ms_class, ss); - if (!dl_tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); - /* FIXME: send reject */ - return -EBUSY; - } - dl_tbf->m_tlli = tlli; - dl_tbf->m_tlli_valid = 1; - dl_tbf->ta = ta; - - LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf)); - - /* new TBF, so put first frame */ - dl_tbf->m_llc.put_frame(data, len); - dl_tbf->bts->llc_frame_sched(); - - /* Store IMSI for later look-up and PCH retransmission */ - dl_tbf->assign_imsi(imsi); - - /* trigger downlink assignment and set state to ASSIGN. - * we don't use old_downlink, so the possible uplink is used - * to trigger downlink assignment. if there is no uplink, - * AGCH is used. */ - dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi); - return 0; -} - -/** - * TODO: split into unit test-able parts... - */ -int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts, - const uint32_t tlli, const char *imsi, - const uint8_t ms_class, const uint16_t delay_csec, - const uint8_t *data, const uint16_t len) -{ - struct gprs_rlcmac_dl_tbf *dl_tbf; - - /* check for existing TBF */ - dl_tbf = tbf_lookup_dl(bts->bts, tlli, imsi); - if (dl_tbf) { - int rc = dl_tbf->append_data(ms_class, delay_csec, data, len); - if (rc >= 0) - dl_tbf->assign_imsi(imsi); - return rc; - } - - return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, len); -} - gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, int8_t use_trx, uint8_t ms_class, uint32_t tlli, uint8_t ta, struct gprs_rlcmac_tbf *dl_tbf) @@ -682,519 +518,6 @@ int gprs_rlcmac_tbf::rlcmac_diag() return 0; } -struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) -{ - struct msgb *msg; - struct timeval *tv, tv_now; - uint32_t octets = 0, frames = 0; - - gettimeofday(&tv_now, NULL); - - while ((msg = m_llc.dequeue())) { - tv = (struct timeval *)msg->data; - msgb_pull(msg, sizeof(*tv)); - msgb_pull(msg, sizeof(*tv)); - - if (gprs_llc::is_frame_expired(&tv_now, tv)) { - LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU " - "because lifetime limit reached. Queue size %zu\n", - tbf_name(this), m_llc.m_queue_size); - bts->llc_timedout_frame(); - frames++; - octets += msg->len; - msgb_free(msg); - continue; - } - break; - } - - if (frames) { - if (frames > 0xff) - frames = 0xff; - if (octets > 0xffffff) - octets = 0xffffff; - bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets); - } - - return msg; -} - -/* - * Store received block data in LLC message(s) and forward to SGSN - * if complete. - */ -int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data) -{ - const uint8_t *data = _data->block; - uint8_t len = _data->len; - const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data; - uint8_t e, m; - struct rlc_li_field *li; - uint8_t frame_offset[16], offset = 0, chunk; - int i, frames = 0; - - LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len); - - data += 3; - len -= 3; - e = rh->e; /* if extended */ - m = 1; /* more frames, that means: the first frame */ - - /* Parse frame offsets from length indicator(s), if any. */ - while (1) { - if (frames == (int)sizeof(frame_offset)) { - LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in " - "block\n", tbf_name(this)); - return -EINVAL; - } - frame_offset[frames++] = offset; - LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset " - "%d\n", frames, offset); - if (!len) - break; - /* M == 0 and E == 0 is not allowed in this version. */ - if (!m && !e) { - LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA " - "ignored, because M='0' and E='0'.\n", - tbf_name(this)); - return 0; - } - /* no more frames in this segment */ - if (e) { - break; - } - /* There is a new frame and an LI that delimits it. */ - if (m) { - li = (struct rlc_li_field *)data; - LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n", - li->li); - /* Special case: LI == 0 - * If the last segment would fit precisely into the - * rest of the RLC MAC block, there would be no way - * to delimit that this segment ends and is not - * continued in the next block. - * The special LI (0) is used to force the segment to - * extend into the next block, so it is delimited there. - * This LI must be skipped. Also it is the last LI. - */ - if (li->li == 0) { - data++; - len--; - m = 1; /* M is ignored, we know there is more */ - break; /* handle E as '1', so we break! */ - } - e = li->e; - m = li->m; - offset += li->li; - data++; - len--; - continue; - } - } - if (!m) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare " - "data\n"); - } - - LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n", - len); - /* TLLI */ - if (rh->ti) { - if (len < 4) { - LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of " - "frame border\n", tbf_name(this)); - return -EINVAL; - } - data += 4; - len -= 4; - LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: " - "%d\n", len); - } - - /* PFI */ - if (rh->pi) { - LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, " - "please disable in SYSTEM INFORMATION\n"); - if (len < 1) { - LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of " - "frame border\n", tbf_name(this)); - return -EINVAL; - } - data++; - len--; - LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: " - "%d\n", len); - } - - /* Now we have: - * - a list of frames offsets: frame_offset[] - * - number of frames: i - * - m == 0: Last frame carries spare data (end of TBF). - */ - - /* Check if last offset would exceed frame. */ - if (offset > len) { - LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, " - "because LI delimits data that exceeds block size.\n", - tbf_name(this)); - return -EINVAL; - } - - /* create LLC frames */ - for (i = 0; i < frames; i++) { - /* last frame ? */ - if (i == frames - 1) { - /* no more data in last frame */ - if (!m) - break; - /* data until end of frame */ - chunk = len - frame_offset[i]; - } else { - /* data until next frame */ - chunk = frame_offset[i + 1] - frame_offset[i]; - } - if (!m_llc.fits_in_current_frame(chunk)) { - LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds " - "maximum size %u.\n", tbf_name(this), - m_llc.remaining_space()); - chunk = m_llc.remaining_space(); - } - m_llc.append_frame(data + frame_offset[i], chunk); - m_llc.consume(chunk); - /* not last frame. */ - if (i != frames - 1) { - /* send frame to SGSN */ - LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n", - tbf_name(this) , m_llc.frame_length()); - snd_ul_ud(); - m_llc.reset(); - /* also check if CV==0, because the frame may fill up the - * block precisely, then it is also complete. normally the - * frame would be extended into the next block with a 0-length - * delimiter added to this block. */ - } else if (rh->cv == 0) { - /* send frame to SGSN */ - LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame " - "that fits precisely in last block: " - "len=%d\n", tbf_name(this), m_llc.frame_length()); - snd_ul_ud(); - m_llc.reset(); - } - } - - return 0; -} - -/* - * Create DL data block - * The messages are fragmented and forwarded as data blocks. - */ -struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts) -{ - LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. " - "V(S)==%d)\n", tbf_name(this), - m_window.v_a(), m_window.v_s()); - -do_resend: - /* check if there is a block with negative acknowledgement */ - int resend_bsn = m_window.resend_needed(); - if (resend_bsn >= 0) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn); - /* re-send block with negative aknowlegement */ - m_window.m_v_b.mark_unacked(resend_bsn); - bts->rlc_resent(); - return create_dl_acked_block(fn, ts, resend_bsn, false); - } - - /* if the window has stalled, or transfer is complete, - * send an unacknowledged block */ - if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) { - if (state_is(GPRS_RLCMAC_FINISHED)) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " - "because all blocks have been transmitted.\n", - m_window.v_a()); - bts->rlc_restarted(); - } else { - LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " - "because all window is stalled.\n", - m_window.v_a()); - bts->rlc_stalled(); - } - /* If V(S) == V(A) and finished state, we would have received - * acknowledgement of all transmitted block. In this case we - * would have transmitted the final block, and received ack - * from MS. But in this case we did not receive the final ack - * indication from MS. This should never happen if MS works - * correctly. */ - if (m_window.window_empty()) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " - "so we re-transmit final block!\n"); - /* we just send final block again */ - int16_t index = m_window.v_s_mod(-1); - bts->rlc_resent(); - return create_dl_acked_block(fn, ts, index, false); - } - - /* cycle through all unacked blocks */ - int resend = m_window.mark_for_resend(); - - /* At this point there should be at least one unacked block - * to be resent. If not, this is an software error. */ - if (resend == 0) { - LOGP(DRLCMACDL, LOGL_ERROR, "Software error: " - "There are no unacknowledged blocks, but V(A) " - " != V(S). PLEASE FIX!\n"); - /* we just send final block again */ - int16_t index = m_window.v_s_mod(-1); - return create_dl_acked_block(fn, ts, index, false); - } - goto do_resend; - } - - return create_new_bsn(fn, ts); -} - -struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts) -{ - struct rlc_dl_header *rh; - struct rlc_li_field *li; - struct msgb *msg; - uint8_t *delimiter, *data, *e_pointer; - uint16_t space, chunk; - gprs_rlc_data *rlc_data; - bool first_fin_ack = false; - const uint16_t bsn = m_window.v_s(); - - LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n", - m_window.v_s()); - -#warning "Selection of the CS doesn't belong here" - if (cs == 0) { - cs = bts_data()->initial_cs_dl; - if (cs < 1 || cs > 4) - cs = 1; - } - /* total length of block, including spare bits */ - const uint8_t block_length = gprs_rlcmac_cs[cs].block_length; - /* length of usable data of block, w/o spare bits, inc. MAC */ - const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data; - - /* now we still have untransmitted LLC data, so we fill mac block */ - rlc_data = m_rlc.block(bsn); - data = rlc_data->prepare(block_data_len); - - rh = (struct rlc_dl_header *)data; - rh->pt = 0; /* Data Block */ - rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */ - rh->usf = 7; /* will be set at scheduler */ - rh->pr = 0; /* FIXME: power reduction */ - rh->tfi = m_tfi; /* TFI */ - rh->fbi = 0; /* Final Block Indicator, set late, if true */ - rh->bsn = bsn; /* Block Sequence Number */ - rh->e = 0; /* Extension bit, maybe set later */ - e_pointer = data + 2; /* points to E of current chunk */ - data += sizeof(*rh); - delimiter = data; /* where next length header would be stored */ - space = block_data_len - sizeof(*rh); - while (1) { - chunk = m_llc.chunk_size(); - /* if chunk will exceed block limit */ - if (chunk > space) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "larger than space (%d) left in block: copy " - "only remaining space, and we are done\n", - chunk, space); - /* block is filled, so there is no extension */ - *e_pointer |= 0x01; - /* fill only space */ - m_llc.consume(data, space); - /* return data block as message */ - break; - } - /* if FINAL chunk would fit precisely in space left */ - if (chunk == space && llist_empty(&m_llc.queue)) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "would exactly fit into space (%d): because " - "this is a final block, we don't add length " - "header, and we are done\n", chunk, space); - LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " - "%s that fits precisely in last block: " - "len=%d\n", tbf_name(this), m_llc.frame_length()); - gprs_rlcmac_dl_bw(this, m_llc.frame_length()); - /* block is filled, so there is no extension */ - *e_pointer |= 0x01; - /* fill space */ - m_llc.consume(data, space); - m_llc.reset(); - /* final block */ - rh->fbi = 1; /* we indicate final block */ - set_state(GPRS_RLCMAC_FINISHED); - /* return data block as message */ - break; - } - /* if chunk would fit exactly in space left */ - if (chunk == space) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "would exactly fit into space (%d): add length " - "header with LI=0, to make frame extend to " - "next block, and we are done\n", chunk, space); - /* make space for delimiter */ - if (delimiter != data) - memmove(delimiter + 1, delimiter, - data - delimiter); - data++; - space--; - /* add LI with 0 length */ - li = (struct rlc_li_field *)delimiter; - li->e = 1; /* not more extension */ - li->m = 0; /* shall be set to 0, in case of li = 0 */ - li->li = 0; /* chunk fills the complete space */ - // no need to set e_pointer nor increase delimiter - /* fill only space, which is 1 octet less than chunk */ - m_llc.consume(data, space); - /* return data block as message */ - break; - } - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " - "than remaining space (%d): add length header to " - "to delimit LLC frame\n", chunk, space); - /* the LLC frame chunk ends in this block */ - /* make space for delimiter */ - if (delimiter != data) - memmove(delimiter + 1, delimiter, data - delimiter); - data++; - space--; - /* add LI to delimit frame */ - li = (struct rlc_li_field *)delimiter; - li->e = 0; /* Extension bit, maybe set later */ - li->m = 0; /* will be set later, if there is more LLC data */ - li->li = chunk; /* length of chunk */ - e_pointer = delimiter; /* points to E of current delimiter */ - delimiter++; - /* copy (rest of) LLC frame to space and reset later */ - m_llc.consume(data, chunk); - data += chunk; - space -= chunk; - LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s" - "len=%d\n", tbf_name(this), m_llc.frame_length()); - gprs_rlcmac_dl_bw(this, m_llc.frame_length()); - m_llc.reset(); - /* dequeue next LLC frame, if any */ - msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); - if (msg) { - LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " - "%s (len=%d)\n", tbf_name(this), msg->len); - m_llc.put_frame(msg->data, msg->len); - bts->llc_frame_sched(); - msgb_free(msg); - } - /* if we have more data and we have space left */ - if (space > 0 && m_llc.frame_length()) { - li->m = 1; /* we indicate more frames to follow */ - continue; - } - /* if we don't have more LLC frames */ - if (!m_llc.frame_length()) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " - "done.\n"); - li->e = 1; /* we cannot extend */ - rh->fbi = 1; /* we indicate final block */ - first_fin_ack = true; - /* + 1 indicates: first final ack */ - set_state(GPRS_RLCMAC_FINISHED); - break; - } - /* we have no space left */ - LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " - "done.\n"); - li->e = 1; /* we cannot extend */ - break; - } - LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n", - osmo_hexdump(rlc_data->block, block_length)); -#warning "move this up?" - rlc_data->len = block_length; - /* raise send state and set ack state array */ - m_window.m_v_b.mark_unacked(bsn); - m_window.increment_send(); - - return create_dl_acked_block(fn, ts, bsn, first_fin_ack); -} - -struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( - const uint32_t fn, const uint8_t ts, - const int index, const bool first_fin_ack) -{ - uint8_t *data; - struct rlc_dl_header *rh; - struct msgb *dl_msg; - uint8_t len; - - /* get data and header from current block */ - data = m_rlc.block(index)->block; - len = m_rlc.block(index)->len; - rh = (struct rlc_dl_header *)data; - - /* Clear Polling, if still set in history buffer */ - rh->s_p = 0; - - /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx. - */ - if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) { - if (first_fin_ack) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " - "polling, because first final block sent.\n"); - } else { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " - "polling, because %d blocks sent.\n", - POLL_ACK_AFTER_FRAMES); - } - /* scheduling not possible, because: */ - if (poll_state != GPRS_RLCMAC_POLL_NONE) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already " - "sheduled for %s, so we must wait for " - "requesting downlink ack\n", tbf_name(this)); - else if (control_ts != ts) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " - "sheduled in this TS %d, waiting for " - "TS %d\n", ts, control_ts); -#warning "What happens to the first_fin_ack in case something is already scheduled?" - else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648)) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " - "sheduled, because single block alllocation " - "already exists\n"); - else { - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this " - "TS %d\n", ts); - m_tx_counter = 0; - /* start timer whenever we send the final block */ - if (rh->fbi == 1) - tbf_timer_start(this, 3191, bts_data()->t3191, 0); - - /* schedule polling */ - poll_state = GPRS_RLCMAC_POLL_SCHED; - poll_fn = (fn + 13) % 2715648; - - /* set polling in header */ - rh->rrbp = 0; /* N+13 */ - rh->s_p = 1; /* Polling */ - } - } - - /* return data block as message */ - dl_msg = msgb_alloc(len, "rlcmac_dl_data"); - if (!dl_msg) - return NULL; - - /* Increment TX-counter */ - m_tx_counter++; - - memcpy(msgb_put(dl_msg, len), data, len); - bts->rlc_sent(); - - return dl_msg; -} - struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn) { struct msgb *msg; @@ -1348,155 +671,6 @@ struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn) return msg; } -struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn) -{ - int final = (state_is(GPRS_RLCMAC_FINISHED)); - struct msgb *msg; - - if (final) { - if (poll_state != GPRS_RLCMAC_POLL_NONE) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "sheduled for %s, so we must wait for " - "final uplink ack...\n", tbf_name(this)); - return NULL; - } - if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "scheduled for single block allocation...\n"); - return NULL; - } - } - - msg = msgb_alloc(23, "rlcmac_ul_ack"); - if (!msg) - return NULL; - bitvec *ack_vec = bitvec_alloc(23); - if (!ack_vec) { - msgb_free(msg); - return NULL; - } - bitvec_unhex(ack_vec, - "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); - Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final); - encode_gsm_rlcmac_downlink(ack_vec, mac_control_block); - bitvec_pack(ack_vec, msgb_put(msg, 23)); - bitvec_free(ack_vec); - talloc_free(mac_control_block); - - /* now we must set this flag, so we are allowed to assign downlink - * TBF on PACCH. it is only allowed when TLLI is acknowledged. */ - m_contention_resolution_done = 1; - - if (final) { - poll_state = GPRS_RLCMAC_POLL_SCHED; - poll_fn = (fn + 13) % 2715648; - /* waiting for final acknowledge */ - ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; - m_final_ack_sent = 1; - } else - ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; - - return msg; -} - -int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) -{ - int16_t dist; /* must be signed */ - uint16_t lost = 0, received = 0; - char show_rbb[65]; - char show_v_b[RLC_MAX_SNS + 1]; - const uint16_t mod_sns = m_window.mod_sns(); - - Decoding::extract_rbb(rbb, show_rbb); - /* show received array in debug (bit 64..1) */ - LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" - "(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns, - show_rbb, (ssn - 1) & mod_sns); - - /* apply received array to receive state (SSN-64..SSN-1) */ - /* calculate distance of ssn from V(S) */ - dist = (m_window.v_s() - ssn) & mod_sns; - /* check if distance is less than distance V(A)..V(S) */ - if (dist >= m_window.distance()) { - /* this might happpen, if the downlink assignment - * was not received by ms and the ack refers - * to previous TBF - * FIXME: we should implement polling for - * control ack!*/ - LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " - "V(A)..V(S) range %s Free TBF!\n", tbf_name(this)); - return 1; /* indicate to free TBF */ - } - - m_window.update(bts, show_rbb, ssn, - &lost, &received); - - /* report lost and received packets */ - gprs_rlcmac_received_lost(this, received, lost); - - /* raise V(A), if possible */ - m_window.raise(m_window.move_window()); - - /* show receive state array in debug (V(A)..V(S)-1) */ - m_window.show_state(show_v_b); - LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" - "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " - "X=Resend-Unacked I=Invalid\n", - m_window.v_a(), show_v_b, - m_window.v_s_mod(-1)); - - if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) { - LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " - "all blocks, but without final ack " - "inidcation (don't worry)\n"); - } - return 0; -} - - -int gprs_rlcmac_dl_tbf::maybe_start_new_window() -{ - struct msgb *msg; - uint16_t received; - - LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); - /* range V(A)..V(S)-1 */ - received = m_window.count_unacked(); - - /* report all outstanding packets as received */ - gprs_rlcmac_received_lost(this, received, 0); - - set_state(GPRS_RLCMAC_WAIT_RELEASE); - - /* check for LLC PDU in the LLC Queue */ - msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); - if (!msg) { - /* no message, start T3193, change state to RELEASE */ - LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n"); - /* start T3193 */ - tbf_timer_start(this, 3193, - bts_data()->t3193_msec / 1000, - (bts_data()->t3193_msec % 1000) * 1000); - - return 0; - } - - /* we have more data so we will re-use this tbf */ - reuse_tbf(msg->data, msg->len); - msgb_free(msg); - return 0; -} - -int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb) -{ - LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this)); - - if (!final_ack) - return update_window(ssn, rbb); - return maybe_start_new_window(); -} - void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_trx *trx) { for (uint8_t tfi = 0; tfi < 32; tfi++) { @@ -1615,161 +789,6 @@ int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len) return 1; } -int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi) -{ - struct rlc_ul_header *rh = (struct rlc_ul_header *)data; - int rc; - - const uint16_t mod_sns = m_window.mod_sns(); - const uint16_t ws = m_window.ws(); - - this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA); - - LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. " - "V(R)=%d)\n", rh->tfi, this->m_window.v_q(), - this->m_window.v_r()); - - /* process RSSI */ - gprs_rlcmac_rssi(this, rssi); - - /* get TLLI */ - if (!this->is_tlli_valid()) { - if (!extract_tlli(data, len)) - return 0; - /* already have TLLI, but we stille get another one */ - } else if (rh->ti) { - uint32_t tlli; - rc = Decoding::tlli_from_ul_data(data, len, &tlli); - if (rc) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " - "of UL DATA TFI=%d.\n", rh->tfi); - return 0; - } - if (tlli != this->tlli()) { - LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " - "DATA TFI=%d. (Ignoring due to contention " - "resolution)\n", rh->tfi); - return 0; - } - } - - /* restart T3169 */ - tbf_timer_start(this, 3169, bts_data()->t3169, 0); - - /* Increment RX-counter */ - this->m_rx_counter++; - - if (!m_window.is_in_window(rh->bsn)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " - "%d..%d (it's normal)\n", rh->bsn, - m_window.v_q(), - (m_window.v_q() + ws - 1) & mod_sns); - maybe_schedule_uplink_acknack(rh); - return 0; - } - - /* Write block to buffer and set receive state array. */ - m_rlc.block(rh->bsn)->put_data(data, len); - LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", - rh->bsn, m_window.v_q(), - (m_window.v_q() + ws - 1) & mod_sns); - - /* Raise V(Q) if possible, and retrieve LLC frames from blocks. - * This is looped until there is a gap (non received block) or - * the window is empty.*/ - const uint16_t v_q_beg = m_window.v_q(); - - const uint16_t count = m_window.receive_bsn(rh->bsn); - - /* Retrieve LLC frames from blocks that are ready */ - for (uint16_t i = 0; i < count; ++i) { - uint16_t index = (v_q_beg + i) & mod_sns; - assemble_forward_llc(m_rlc.block(index)); - } - - /* Check CV of last frame in buffer */ - if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */ - && this->m_window.v_q() == this->m_window.v_r()) { /* if complete */ - struct rlc_ul_header *last_rh = (struct rlc_ul_header *) - m_rlc.block((m_window.v_r() - 1) & mod_sns)->block; - LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " - "last block: BSN=%d CV=%d\n", last_rh->bsn, - last_rh->cv); - if (last_rh->cv == 0) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " - "TBF\n"); - set_state(GPRS_RLCMAC_FINISHED); - /* Reset N3103 counter. */ - this->m_n3103 = 0; - } - } - - /* If TLLI is included or if we received half of the window, we send - * an ack/nack */ - maybe_schedule_uplink_acknack(rh); - - return 0; -} - -void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh) -{ - if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED) - || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { - if (rh->si) { - LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " - "because MS is stalled.\n"); - } - if (rh->ti) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because TLLI is included.\n"); - } - if (state_is(GPRS_RLCMAC_FINISHED)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because last block has CV==0.\n"); - } - if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because %d frames received.\n", - SEND_ACK_AFTER_FRAMES); - } - if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) { - /* trigger sending at next RTS */ - ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; - } else { - /* already triggered */ - LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is " - "already triggered, don't schedule!\n"); - } - } -} - -/* Send Uplink unit-data to SGSN. */ -int gprs_rlcmac_ul_tbf::snd_ul_ud() -{ - uint8_t qos_profile[3]; - struct msgb *llc_pdu; - unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length(); - struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); - - LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length()); - if (!bctx) { - LOGP(DBSSGP, LOGL_ERROR, "No bctx\n"); - m_llc.reset_frame_space(); - return -EIO; - } - - llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu"); - uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length())); - tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame); - qos_profile[0] = QOS_PROFILE >> 16; - qos_profile[1] = QOS_PROFILE >> 8; - qos_profile[2] = QOS_PROFILE; - bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu); - - m_llc.reset_frame_space(); - return 0; -} - const char *tbf_name(gprs_rlcmac_tbf *tbf) { static char buf[40]; @@ -1780,35 +799,6 @@ const char *tbf_name(gprs_rlcmac_tbf *tbf) return buf; } - -void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len) -{ - bts->tbf_reused(); - m_llc.put_frame(data, len); - bts->llc_frame_sched(); - - /* reset rlc states */ - m_tx_counter = 0; - m_wait_confirm = 0; - m_window.reset(); - - /* keep to flags */ - state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; - state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); - - update(); - - LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, " - "because another LLC PDU has arrived in between\n", - tbf_name(this)); - bts->trigger_dl_ass(this, this, NULL); -} - -bool gprs_rlcmac_dl_tbf::dl_window_stalled() const -{ - return m_window.window_stalled(); -} - void gprs_rlcmac_tbf::rotate_in_list() { llist_del(&list.list); diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp new file mode 100644 index 00000000..8abea201 --- /dev/null +++ b/src/tbf_dl.cpp @@ -0,0 +1,671 @@ +/* Copied from tbf.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +} + +#include +#include + +/* After sending these frames, we poll for ack/nack. */ +#define POLL_ACK_AFTER_FRAMES 20 + + +static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = { +/* frame length data block max payload */ + { 0, 0, 0 }, + { 23, 23, 20 }, /* CS-1 */ + { 34, 33, 30 }, /* CS-2 */ + { 40, 39, 36 }, /* CS-3 */ + { 54, 53, 50 }, /* CS-4 */ +}; + +extern "C" { +int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, + uint8_t num_frames, uint32_t num_octets); +} + +static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf, + const uint8_t ms_class) +{ + if (!tbf->ms_class && ms_class) + tbf->ms_class = ms_class; +} + +int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class, + const uint16_t pdu_delay_csec, + const uint8_t *data, const uint16_t len) +{ + LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this)); + if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) { + LOGP(DRLCMAC, LOGL_DEBUG, + "%s in WAIT RELEASE state " + "(T3193), so reuse TBF\n", tbf_name(this)); + tbf_update_ms_class(this, ms_class); + reuse_tbf(data, len); + } else { + /* the TBF exists, so we must write it in the queue + * we prepend lifetime in front of PDU */ + struct timeval *tv; + struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2, + "llc_pdu_queue"); + if (!llc_msg) + return -ENOMEM; + tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); + gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv); + tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); + gettimeofday(tv, NULL); + memcpy(msgb_put(llc_msg, len), data, len); + m_llc.enqueue(llc_msg); + tbf_update_ms_class(this, ms_class); + } + + return 0; +} + +static struct gprs_rlcmac_dl_tbf *tbf_lookup_dl(BTS *bts, + const uint32_t tlli, const char *imsi) +{ + /* TODO: look up by IMSI first, then tlli, then old_tlli */ + return bts->dl_tbf_by_tlli(tlli); +} + +static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, + const char *imsi, + const uint32_t tlli, const uint8_t ms_class, + const uint8_t *data, const uint16_t len) +{ + uint8_t trx, ta, ss; + int8_t use_trx; + struct gprs_rlcmac_ul_tbf *ul_tbf, *old_ul_tbf; + struct gprs_rlcmac_dl_tbf *dl_tbf; + int8_t tfi; /* must be signed */ + int rc; + + /* check for uplink data, so we copy our informations */ +#warning "Do the same look up for IMSI, TLLI and OLD_TLLI" +#warning "Refactor the below lines... into a new method" + ul_tbf = bts->bts->ul_tbf_by_tlli(tlli); + if (ul_tbf && ul_tbf->m_contention_resolution_done + && !ul_tbf->m_final_ack_sent) { + use_trx = ul_tbf->trx->trx_no; + ta = ul_tbf->ta; + ss = 0; + old_ul_tbf = ul_tbf; + } else { + use_trx = -1; + /* we already have an uplink TBF, so we use that TA */ + if (ul_tbf) + ta = ul_tbf->ta; + else { + /* recall TA */ + rc = bts->bts->timing_advance()->recall(tlli); + if (rc < 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown" + ", assuming 0\n"); + ta = 0; + } else + ta = rc; + } + ss = 1; /* PCH assignment only allows one timeslot */ + old_ul_tbf = NULL; + } + + // Create new TBF (any TRX) +#warning "Copy and paste with alloc_ul_tbf" + tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx); + if (tfi < 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); + /* FIXME: send reject */ + return -EBUSY; + } + /* set number of downlink slots according to multislot class */ + dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf, tfi, trx, ms_class, ss); + if (!dl_tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); + /* FIXME: send reject */ + return -EBUSY; + } + dl_tbf->m_tlli = tlli; + dl_tbf->m_tlli_valid = 1; + dl_tbf->ta = ta; + + LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf)); + + /* new TBF, so put first frame */ + dl_tbf->m_llc.put_frame(data, len); + dl_tbf->bts->llc_frame_sched(); + + /* Store IMSI for later look-up and PCH retransmission */ + dl_tbf->assign_imsi(imsi); + + /* trigger downlink assignment and set state to ASSIGN. + * we don't use old_downlink, so the possible uplink is used + * to trigger downlink assignment. if there is no uplink, + * AGCH is used. */ + dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi); + return 0; +} + +/** + * TODO: split into unit test-able parts... + */ +int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts, + const uint32_t tlli, const char *imsi, + const uint8_t ms_class, const uint16_t delay_csec, + const uint8_t *data, const uint16_t len) +{ + struct gprs_rlcmac_dl_tbf *dl_tbf; + + /* check for existing TBF */ + dl_tbf = tbf_lookup_dl(bts->bts, tlli, imsi); + if (dl_tbf) { + int rc = dl_tbf->append_data(ms_class, delay_csec, data, len); + if (rc >= 0) + dl_tbf->assign_imsi(imsi); + return rc; + } + + return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, len); +} + +struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) +{ + struct msgb *msg; + struct timeval *tv, tv_now; + uint32_t octets = 0, frames = 0; + + gettimeofday(&tv_now, NULL); + + while ((msg = m_llc.dequeue())) { + tv = (struct timeval *)msg->data; + msgb_pull(msg, sizeof(*tv)); + msgb_pull(msg, sizeof(*tv)); + + if (gprs_llc::is_frame_expired(&tv_now, tv)) { + LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU " + "because lifetime limit reached. Queue size %zu\n", + tbf_name(this), m_llc.m_queue_size); + bts->llc_timedout_frame(); + frames++; + octets += msg->len; + msgb_free(msg); + continue; + } + break; + } + + if (frames) { + if (frames > 0xff) + frames = 0xff; + if (octets > 0xffffff) + octets = 0xffffff; + bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets); + } + + return msg; +} + +/* + * Create DL data block + * The messages are fragmented and forwarded as data blocks. + */ +struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts) +{ + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. " + "V(S)==%d)\n", tbf_name(this), + m_window.v_a(), m_window.v_s()); + +do_resend: + /* check if there is a block with negative acknowledgement */ + int resend_bsn = m_window.resend_needed(); + if (resend_bsn >= 0) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn); + /* re-send block with negative aknowlegement */ + m_window.m_v_b.mark_unacked(resend_bsn); + bts->rlc_resent(); + return create_dl_acked_block(fn, ts, resend_bsn, false); + } + + /* if the window has stalled, or transfer is complete, + * send an unacknowledged block */ + if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) { + if (state_is(GPRS_RLCMAC_FINISHED)) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " + "because all blocks have been transmitted.\n", + m_window.v_a()); + bts->rlc_restarted(); + } else { + LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " + "because all window is stalled.\n", + m_window.v_a()); + bts->rlc_stalled(); + } + /* If V(S) == V(A) and finished state, we would have received + * acknowledgement of all transmitted block. In this case we + * would have transmitted the final block, and received ack + * from MS. But in this case we did not receive the final ack + * indication from MS. This should never happen if MS works + * correctly. */ + if (m_window.window_empty()) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " + "so we re-transmit final block!\n"); + /* we just send final block again */ + int16_t index = m_window.v_s_mod(-1); + bts->rlc_resent(); + return create_dl_acked_block(fn, ts, index, false); + } + + /* cycle through all unacked blocks */ + int resend = m_window.mark_for_resend(); + + /* At this point there should be at least one unacked block + * to be resent. If not, this is an software error. */ + if (resend == 0) { + LOGP(DRLCMACDL, LOGL_ERROR, "Software error: " + "There are no unacknowledged blocks, but V(A) " + " != V(S). PLEASE FIX!\n"); + /* we just send final block again */ + int16_t index = m_window.v_s_mod(-1); + return create_dl_acked_block(fn, ts, index, false); + } + goto do_resend; + } + + return create_new_bsn(fn, ts); +} + +struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts) +{ + struct rlc_dl_header *rh; + struct rlc_li_field *li; + struct msgb *msg; + uint8_t *delimiter, *data, *e_pointer; + uint16_t space, chunk; + gprs_rlc_data *rlc_data; + bool first_fin_ack = false; + const uint16_t bsn = m_window.v_s(); + + LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n", + m_window.v_s()); + +#warning "Selection of the CS doesn't belong here" + if (cs == 0) { + cs = bts_data()->initial_cs_dl; + if (cs < 1 || cs > 4) + cs = 1; + } + /* total length of block, including spare bits */ + const uint8_t block_length = gprs_rlcmac_cs[cs].block_length; + /* length of usable data of block, w/o spare bits, inc. MAC */ + const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data; + + /* now we still have untransmitted LLC data, so we fill mac block */ + rlc_data = m_rlc.block(bsn); + data = rlc_data->prepare(block_data_len); + + rh = (struct rlc_dl_header *)data; + rh->pt = 0; /* Data Block */ + rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */ + rh->usf = 7; /* will be set at scheduler */ + rh->pr = 0; /* FIXME: power reduction */ + rh->tfi = m_tfi; /* TFI */ + rh->fbi = 0; /* Final Block Indicator, set late, if true */ + rh->bsn = bsn; /* Block Sequence Number */ + rh->e = 0; /* Extension bit, maybe set later */ + e_pointer = data + 2; /* points to E of current chunk */ + data += sizeof(*rh); + delimiter = data; /* where next length header would be stored */ + space = block_data_len - sizeof(*rh); + while (1) { + chunk = m_llc.chunk_size(); + /* if chunk will exceed block limit */ + if (chunk > space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "larger than space (%d) left in block: copy " + "only remaining space, and we are done\n", + chunk, space); + /* block is filled, so there is no extension */ + *e_pointer |= 0x01; + /* fill only space */ + m_llc.consume(data, space); + /* return data block as message */ + break; + } + /* if FINAL chunk would fit precisely in space left */ + if (chunk == space && llist_empty(&m_llc.queue)) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): because " + "this is a final block, we don't add length " + "header, and we are done\n", chunk, space); + LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " + "%s that fits precisely in last block: " + "len=%d\n", tbf_name(this), m_llc.frame_length()); + gprs_rlcmac_dl_bw(this, m_llc.frame_length()); + /* block is filled, so there is no extension */ + *e_pointer |= 0x01; + /* fill space */ + m_llc.consume(data, space); + m_llc.reset(); + /* final block */ + rh->fbi = 1; /* we indicate final block */ + set_state(GPRS_RLCMAC_FINISHED); + /* return data block as message */ + break; + } + /* if chunk would fit exactly in space left */ + if (chunk == space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): add length " + "header with LI=0, to make frame extend to " + "next block, and we are done\n", chunk, space); + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, + data - delimiter); + data++; + space--; + /* add LI with 0 length */ + li = (struct rlc_li_field *)delimiter; + li->e = 1; /* not more extension */ + li->m = 0; /* shall be set to 0, in case of li = 0 */ + li->li = 0; /* chunk fills the complete space */ + // no need to set e_pointer nor increase delimiter + /* fill only space, which is 1 octet less than chunk */ + m_llc.consume(data, space); + /* return data block as message */ + break; + } + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " + "than remaining space (%d): add length header to " + "to delimit LLC frame\n", chunk, space); + /* the LLC frame chunk ends in this block */ + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, data - delimiter); + data++; + space--; + /* add LI to delimit frame */ + li = (struct rlc_li_field *)delimiter; + li->e = 0; /* Extension bit, maybe set later */ + li->m = 0; /* will be set later, if there is more LLC data */ + li->li = chunk; /* length of chunk */ + e_pointer = delimiter; /* points to E of current delimiter */ + delimiter++; + /* copy (rest of) LLC frame to space and reset later */ + m_llc.consume(data, chunk); + data += chunk; + space -= chunk; + LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s" + "len=%d\n", tbf_name(this), m_llc.frame_length()); + gprs_rlcmac_dl_bw(this, m_llc.frame_length()); + m_llc.reset(); + /* dequeue next LLC frame, if any */ + msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); + if (msg) { + LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " + "%s (len=%d)\n", tbf_name(this), msg->len); + m_llc.put_frame(msg->data, msg->len); + bts->llc_frame_sched(); + msgb_free(msg); + } + /* if we have more data and we have space left */ + if (space > 0 && m_llc.frame_length()) { + li->m = 1; /* we indicate more frames to follow */ + continue; + } + /* if we don't have more LLC frames */ + if (!m_llc.frame_length()) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " + "done.\n"); + li->e = 1; /* we cannot extend */ + rh->fbi = 1; /* we indicate final block */ + first_fin_ack = true; + /* + 1 indicates: first final ack */ + set_state(GPRS_RLCMAC_FINISHED); + break; + } + /* we have no space left */ + LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " + "done.\n"); + li->e = 1; /* we cannot extend */ + break; + } + LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n", + osmo_hexdump(rlc_data->block, block_length)); +#warning "move this up?" + rlc_data->len = block_length; + /* raise send state and set ack state array */ + m_window.m_v_b.mark_unacked(bsn); + m_window.increment_send(); + + return create_dl_acked_block(fn, ts, bsn, first_fin_ack); +} + +struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( + const uint32_t fn, const uint8_t ts, + const int index, const bool first_fin_ack) +{ + uint8_t *data; + struct rlc_dl_header *rh; + struct msgb *dl_msg; + uint8_t len; + + /* get data and header from current block */ + data = m_rlc.block(index)->block; + len = m_rlc.block(index)->len; + rh = (struct rlc_dl_header *)data; + + /* Clear Polling, if still set in history buffer */ + rh->s_p = 0; + + /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx. + */ + if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) { + if (first_fin_ack) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " + "polling, because first final block sent.\n"); + } else { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " + "polling, because %d blocks sent.\n", + POLL_ACK_AFTER_FRAMES); + } + /* scheduling not possible, because: */ + if (poll_state != GPRS_RLCMAC_POLL_NONE) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already " + "sheduled for %s, so we must wait for " + "requesting downlink ack\n", tbf_name(this)); + else if (control_ts != ts) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " + "sheduled in this TS %d, waiting for " + "TS %d\n", ts, control_ts); +#warning "What happens to the first_fin_ack in case something is already scheduled?" + else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648)) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " + "sheduled, because single block alllocation " + "already exists\n"); + else { + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this " + "TS %d\n", ts); + m_tx_counter = 0; + /* start timer whenever we send the final block */ + if (rh->fbi == 1) + tbf_timer_start(this, 3191, bts_data()->t3191, 0); + + /* schedule polling */ + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + + /* set polling in header */ + rh->rrbp = 0; /* N+13 */ + rh->s_p = 1; /* Polling */ + } + } + + /* return data block as message */ + dl_msg = msgb_alloc(len, "rlcmac_dl_data"); + if (!dl_msg) + return NULL; + + /* Increment TX-counter */ + m_tx_counter++; + + memcpy(msgb_put(dl_msg, len), data, len); + bts->rlc_sent(); + + return dl_msg; +} + +int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) +{ + int16_t dist; /* must be signed */ + uint16_t lost = 0, received = 0; + char show_rbb[65]; + char show_v_b[RLC_MAX_SNS + 1]; + const uint16_t mod_sns = m_window.mod_sns(); + + Decoding::extract_rbb(rbb, show_rbb); + /* show received array in debug (bit 64..1) */ + LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" + "(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns, + show_rbb, (ssn - 1) & mod_sns); + + /* apply received array to receive state (SSN-64..SSN-1) */ + /* calculate distance of ssn from V(S) */ + dist = (m_window.v_s() - ssn) & mod_sns; + /* check if distance is less than distance V(A)..V(S) */ + if (dist >= m_window.distance()) { + /* this might happpen, if the downlink assignment + * was not received by ms and the ack refers + * to previous TBF + * FIXME: we should implement polling for + * control ack!*/ + LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " + "V(A)..V(S) range %s Free TBF!\n", tbf_name(this)); + return 1; /* indicate to free TBF */ + } + + m_window.update(bts, show_rbb, ssn, + &lost, &received); + + /* report lost and received packets */ + gprs_rlcmac_received_lost(this, received, lost); + + /* raise V(A), if possible */ + m_window.raise(m_window.move_window()); + + /* show receive state array in debug (V(A)..V(S)-1) */ + m_window.show_state(show_v_b); + LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" + "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " + "X=Resend-Unacked I=Invalid\n", + m_window.v_a(), show_v_b, + m_window.v_s_mod(-1)); + + if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) { + LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " + "all blocks, but without final ack " + "inidcation (don't worry)\n"); + } + return 0; +} + + +int gprs_rlcmac_dl_tbf::maybe_start_new_window() +{ + struct msgb *msg; + uint16_t received; + + LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); + /* range V(A)..V(S)-1 */ + received = m_window.count_unacked(); + + /* report all outstanding packets as received */ + gprs_rlcmac_received_lost(this, received, 0); + + set_state(GPRS_RLCMAC_WAIT_RELEASE); + + /* check for LLC PDU in the LLC Queue */ + msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); + if (!msg) { + /* no message, start T3193, change state to RELEASE */ + LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n"); + /* start T3193 */ + tbf_timer_start(this, 3193, + bts_data()->t3193_msec / 1000, + (bts_data()->t3193_msec % 1000) * 1000); + + return 0; + } + + /* we have more data so we will re-use this tbf */ + reuse_tbf(msg->data, msg->len); + msgb_free(msg); + return 0; +} + +int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb) +{ + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this)); + + if (!final_ack) + return update_window(ssn, rbb); + return maybe_start_new_window(); +} + +void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len) +{ + bts->tbf_reused(); + m_llc.put_frame(data, len); + bts->llc_frame_sched(); + + /* reset rlc states */ + m_tx_counter = 0; + m_wait_confirm = 0; + m_window.reset(); + + /* keep to flags */ + state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; + state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); + + update(); + + LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, " + "because another LLC PDU has arrived in between\n", + tbf_name(this)); + bts->trigger_dl_ass(this, this, NULL); +} + +bool gprs_rlcmac_dl_tbf::dl_window_stalled() const +{ + return m_window.window_stalled(); +} + diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp new file mode 100644 index 00000000..3ab71f05 --- /dev/null +++ b/src/tbf_ul.cpp @@ -0,0 +1,418 @@ +/* Copied from tbf.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +} + +#include +#include + +/* After receiving these frames, we send ack/nack. */ +#define SEND_ACK_AFTER_FRAMES 20 + +extern void *tall_pcu_ctx; + + +/* + * Store received block data in LLC message(s) and forward to SGSN + * if complete. + */ +int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data) +{ + const uint8_t *data = _data->block; + uint8_t len = _data->len; + const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data; + uint8_t e, m; + struct rlc_li_field *li; + uint8_t frame_offset[16], offset = 0, chunk; + int i, frames = 0; + + LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len); + + data += 3; + len -= 3; + e = rh->e; /* if extended */ + m = 1; /* more frames, that means: the first frame */ + + /* Parse frame offsets from length indicator(s), if any. */ + while (1) { + if (frames == (int)sizeof(frame_offset)) { + LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in " + "block\n", tbf_name(this)); + return -EINVAL; + } + frame_offset[frames++] = offset; + LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset " + "%d\n", frames, offset); + if (!len) + break; + /* M == 0 and E == 0 is not allowed in this version. */ + if (!m && !e) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA " + "ignored, because M='0' and E='0'.\n", + tbf_name(this)); + return 0; + } + /* no more frames in this segment */ + if (e) { + break; + } + /* There is a new frame and an LI that delimits it. */ + if (m) { + li = (struct rlc_li_field *)data; + LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n", + li->li); + /* Special case: LI == 0 + * If the last segment would fit precisely into the + * rest of the RLC MAC block, there would be no way + * to delimit that this segment ends and is not + * continued in the next block. + * The special LI (0) is used to force the segment to + * extend into the next block, so it is delimited there. + * This LI must be skipped. Also it is the last LI. + */ + if (li->li == 0) { + data++; + len--; + m = 1; /* M is ignored, we know there is more */ + break; /* handle E as '1', so we break! */ + } + e = li->e; + m = li->m; + offset += li->li; + data++; + len--; + continue; + } + } + if (!m) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare " + "data\n"); + } + + LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n", + len); + /* TLLI */ + if (rh->ti) { + if (len < 4) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of " + "frame border\n", tbf_name(this)); + return -EINVAL; + } + data += 4; + len -= 4; + LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: " + "%d\n", len); + } + + /* PFI */ + if (rh->pi) { + LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, " + "please disable in SYSTEM INFORMATION\n"); + if (len < 1) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of " + "frame border\n", tbf_name(this)); + return -EINVAL; + } + data++; + len--; + LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: " + "%d\n", len); + } + + /* Now we have: + * - a list of frames offsets: frame_offset[] + * - number of frames: i + * - m == 0: Last frame carries spare data (end of TBF). + */ + + /* Check if last offset would exceed frame. */ + if (offset > len) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, " + "because LI delimits data that exceeds block size.\n", + tbf_name(this)); + return -EINVAL; + } + + /* create LLC frames */ + for (i = 0; i < frames; i++) { + /* last frame ? */ + if (i == frames - 1) { + /* no more data in last frame */ + if (!m) + break; + /* data until end of frame */ + chunk = len - frame_offset[i]; + } else { + /* data until next frame */ + chunk = frame_offset[i + 1] - frame_offset[i]; + } + if (!m_llc.fits_in_current_frame(chunk)) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds " + "maximum size %u.\n", tbf_name(this), + m_llc.remaining_space()); + chunk = m_llc.remaining_space(); + } + m_llc.append_frame(data + frame_offset[i], chunk); + m_llc.consume(chunk); + /* not last frame. */ + if (i != frames - 1) { + /* send frame to SGSN */ + LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n", + tbf_name(this) , m_llc.frame_length()); + snd_ul_ud(); + m_llc.reset(); + /* also check if CV==0, because the frame may fill up the + * block precisely, then it is also complete. normally the + * frame would be extended into the next block with a 0-length + * delimiter added to this block. */ + } else if (rh->cv == 0) { + /* send frame to SGSN */ + LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame " + "that fits precisely in last block: " + "len=%d\n", tbf_name(this), m_llc.frame_length()); + snd_ul_ud(); + m_llc.reset(); + } + } + + return 0; +} + + +struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn) +{ + int final = (state_is(GPRS_RLCMAC_FINISHED)); + struct msgb *msg; + + if (final) { + if (poll_state != GPRS_RLCMAC_POLL_NONE) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "sheduled for %s, so we must wait for " + "final uplink ack...\n", tbf_name(this)); + return NULL; + } + if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "scheduled for single block allocation...\n"); + return NULL; + } + } + + msg = msgb_alloc(23, "rlcmac_ul_ack"); + if (!msg) + return NULL; + bitvec *ack_vec = bitvec_alloc(23); + if (!ack_vec) { + msgb_free(msg); + return NULL; + } + bitvec_unhex(ack_vec, + "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); + Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final); + encode_gsm_rlcmac_downlink(ack_vec, mac_control_block); + bitvec_pack(ack_vec, msgb_put(msg, 23)); + bitvec_free(ack_vec); + talloc_free(mac_control_block); + + /* now we must set this flag, so we are allowed to assign downlink + * TBF on PACCH. it is only allowed when TLLI is acknowledged. */ + m_contention_resolution_done = 1; + + if (final) { + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + /* waiting for final acknowledge */ + ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; + m_final_ack_sent = 1; + } else + ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; + + return msg; +} + +int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi) +{ + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + int rc; + + const uint16_t mod_sns = m_window.mod_sns(); + const uint16_t ws = m_window.ws(); + + this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA); + + LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. " + "V(R)=%d)\n", rh->tfi, this->m_window.v_q(), + this->m_window.v_r()); + + /* process RSSI */ + gprs_rlcmac_rssi(this, rssi); + + /* get TLLI */ + if (!this->is_tlli_valid()) { + if (!extract_tlli(data, len)) + return 0; + /* already have TLLI, but we stille get another one */ + } else if (rh->ti) { + uint32_t tlli; + rc = Decoding::tlli_from_ul_data(data, len, &tlli); + if (rc) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " + "of UL DATA TFI=%d.\n", rh->tfi); + return 0; + } + if (tlli != this->tlli()) { + LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " + "DATA TFI=%d. (Ignoring due to contention " + "resolution)\n", rh->tfi); + return 0; + } + } + + /* restart T3169 */ + tbf_timer_start(this, 3169, bts_data()->t3169, 0); + + /* Increment RX-counter */ + this->m_rx_counter++; + + if (!m_window.is_in_window(rh->bsn)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " + "%d..%d (it's normal)\n", rh->bsn, + m_window.v_q(), + (m_window.v_q() + ws - 1) & mod_sns); + maybe_schedule_uplink_acknack(rh); + return 0; + } + + /* Write block to buffer and set receive state array. */ + m_rlc.block(rh->bsn)->put_data(data, len); + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", + rh->bsn, m_window.v_q(), + (m_window.v_q() + ws - 1) & mod_sns); + + /* Raise V(Q) if possible, and retrieve LLC frames from blocks. + * This is looped until there is a gap (non received block) or + * the window is empty.*/ + const uint16_t v_q_beg = m_window.v_q(); + + const uint16_t count = m_window.receive_bsn(rh->bsn); + + /* Retrieve LLC frames from blocks that are ready */ + for (uint16_t i = 0; i < count; ++i) { + uint16_t index = (v_q_beg + i) & mod_sns; + assemble_forward_llc(m_rlc.block(index)); + } + + /* Check CV of last frame in buffer */ + if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */ + && this->m_window.v_q() == this->m_window.v_r()) { /* if complete */ + struct rlc_ul_header *last_rh = (struct rlc_ul_header *) + m_rlc.block((m_window.v_r() - 1) & mod_sns)->block; + LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " + "last block: BSN=%d CV=%d\n", last_rh->bsn, + last_rh->cv); + if (last_rh->cv == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " + "TBF\n"); + set_state(GPRS_RLCMAC_FINISHED); + /* Reset N3103 counter. */ + this->m_n3103 = 0; + } + } + + /* If TLLI is included or if we received half of the window, we send + * an ack/nack */ + maybe_schedule_uplink_acknack(rh); + + return 0; +} + +void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh) +{ + if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED) + || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + if (rh->si) { + LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " + "because MS is stalled.\n"); + } + if (rh->ti) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because TLLI is included.\n"); + } + if (state_is(GPRS_RLCMAC_FINISHED)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because last block has CV==0.\n"); + } + if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because %d frames received.\n", + SEND_ACK_AFTER_FRAMES); + } + if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) { + /* trigger sending at next RTS */ + ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; + } else { + /* already triggered */ + LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is " + "already triggered, don't schedule!\n"); + } + } +} + +/* Send Uplink unit-data to SGSN. */ +int gprs_rlcmac_ul_tbf::snd_ul_ud() +{ + uint8_t qos_profile[3]; + struct msgb *llc_pdu; + unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length(); + struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); + + LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length()); + if (!bctx) { + LOGP(DBSSGP, LOGL_ERROR, "No bctx\n"); + m_llc.reset_frame_space(); + return -EIO; + } + + llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu"); + uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length())); + tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame); + qos_profile[0] = QOS_PROFILE >> 16; + qos_profile[1] = QOS_PROFILE >> 8; + qos_profile[2] = QOS_PROFILE; + bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu); + + m_llc.reset_frame_space(); + return 0; +} +