diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bts.h | 1 | ||||
-rw-r--r-- | src/pcu_vty.c | 34 | ||||
-rw-r--r-- | src/tbf.cpp | 1 | ||||
-rw-r--r-- | src/tbf.h | 5 | ||||
-rw-r--r-- | src/tbf_dl.cpp | 35 |
5 files changed, 75 insertions, 1 deletions
@@ -119,6 +119,7 @@ struct gprs_rlcmac_bts { uint8_t force_cs; /* 0=use from BTS 1=use from VTY */ uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */ uint32_t llc_discard_csec; + uint32_t llc_idle_ack_csec; uint8_t t3142; uint8_t t3169; uint8_t t3191; diff --git a/src/pcu_vty.c b/src/pcu_vty.c index 3d9c79a3..00cd6fca 100644 --- a/src/pcu_vty.c +++ b/src/pcu_vty.c @@ -100,6 +100,9 @@ static int config_write_pcu(struct vty *vty) if (bts->llc_discard_csec) vty_out(vty, " queue hysteresis %d%s", bts->llc_discard_csec, VTY_NEWLINE); + if (bts->llc_idle_ack_csec) + vty_out(vty, " queue idle-ack-delay %d%s", bts->llc_idle_ack_csec, + VTY_NEWLINE); if (bts->alloc_algorithm == alloc_algorithm_a) vty_out(vty, " alloc-algorithm a%s", VTY_NEWLINE); if (bts->alloc_algorithm == alloc_algorithm_b) @@ -217,7 +220,7 @@ DEFUN(cfg_pcu_no_queue_lifetime, DEFUN(cfg_pcu_queue_hysteresis, cfg_pcu_queue_hysteresis_cmd, - "queue hysteresis <1-65534>", + "queue hysteresis <1-65535>", QUEUE_STR QUEUE_HYSTERESIS_STR "Hysteresis in centi-seconds") { struct gprs_rlcmac_bts *bts = bts_main_data(); @@ -240,6 +243,33 @@ DEFUN(cfg_pcu_no_queue_hysteresis, return CMD_SUCCESS; } +#define QUEUE_IDLE_ACK_STR "Request an ACK after the last DL LLC frame in centi-seconds\n" + +DEFUN(cfg_pcu_queue_idle_ack_delay, + cfg_pcu_queue_idle_ack_delay_cmd, + "queue idle-ack-delay <1-65535>", + QUEUE_STR QUEUE_IDLE_ACK_STR "Idle ACK delay in centi-seconds") +{ + struct gprs_rlcmac_bts *bts = bts_main_data(); + uint8_t csec = atoi(argv[0]); + + bts->llc_idle_ack_csec = csec; + + return CMD_SUCCESS; +} + +DEFUN(cfg_pcu_no_queue_idle_ack_delay, + cfg_pcu_no_queue_idle_ack_delay_cmd, + "no queue idle-ack-delay", + NO_STR QUEUE_STR QUEUE_IDLE_ACK_STR) +{ + struct gprs_rlcmac_bts *bts = bts_main_data(); + + bts->llc_idle_ack_csec = 0; + + return CMD_SUCCESS; +} + DEFUN(cfg_pcu_alloc, cfg_pcu_alloc_cmd, @@ -400,6 +430,8 @@ int pcu_vty_init(const struct log_info *cat) install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd); install_element(PCU_NODE, &cfg_pcu_queue_hysteresis_cmd); install_element(PCU_NODE, &cfg_pcu_no_queue_hysteresis_cmd); + install_element(PCU_NODE, &cfg_pcu_queue_idle_ack_delay_cmd); + install_element(PCU_NODE, &cfg_pcu_no_queue_idle_ack_delay_cmd); install_element(PCU_NODE, &cfg_pcu_alloc_cmd); install_element(PCU_NODE, &cfg_pcu_two_phase_cmd); install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd); diff --git a/src/tbf.cpp b/src/tbf.cpp index 2c7bd949..9869ff4b 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -128,6 +128,7 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf) if (tbf->direction == GPRS_RLCMAC_DL_TBF) { gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(tbf); gprs_rlcmac_lost_rep(dl_tbf); + dl_tbf->cleanup(); } LOGP(DRLCMAC, LOGL_INFO, "%s free\n", tbf_name(tbf)); @@ -311,6 +311,8 @@ inline time_t gprs_rlcmac_tbf::created_ts() const } struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf { + void cleanup(); + /* dispatch Unitdata.DL messages */ static int handle(struct gprs_rlcmac_bts *bts, const uint32_t tlli, const char *imsi, const uint8_t ms_class, @@ -365,6 +367,9 @@ protected: int maybe_start_new_window(); bool dl_window_stalled() const; void reuse_tbf(const uint8_t *data, const uint16_t len); + void start_llc_timer(); + + struct osmo_timer_list m_llc_timer; }; struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf { diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp index aeec23f2..e8c5dfea 100644 --- a/src/tbf_dl.cpp +++ b/src/tbf_dl.cpp @@ -62,6 +62,38 @@ static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf, tbf->ms_class = ms_class; } +static void llc_timer_cb(void *_tbf) +{ + struct gprs_rlcmac_dl_tbf *tbf = (struct gprs_rlcmac_dl_tbf *)_tbf; + + if (tbf->state_is_not(GPRS_RLCMAC_FLOW)) + return; + + LOGP(DRLCMAC, LOGL_DEBUG, + "%s LLC receive timeout, requesting DL ACK\n", tbf_name(tbf)); + + tbf->request_dl_ack(); +} + +void gprs_rlcmac_dl_tbf::cleanup() +{ + osmo_timer_del(&m_llc_timer); +} + +void gprs_rlcmac_dl_tbf::start_llc_timer() +{ + if (bts_data()->llc_idle_ack_csec > 0) { + struct timeval tv; + + /* TODO: this ought to be within a constructor */ + m_llc_timer.data = this; + m_llc_timer.cb = &llc_timer_cb; + + csecs_to_timeval(bts_data()->llc_idle_ack_csec, &tv); + osmo_timer_schedule(&m_llc_timer, tv.tv_sec, tv.tv_usec); + } +} + 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) @@ -80,7 +112,9 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class, /* it is no longer drained */ m_last_dl_drained_fn = -1; tbf_update_ms_class(this, ms_class); + start_llc_timer(); } else { + /* TODO: put this path into an llc_enqueue method */ /* the TBF exists, so we must write it in the queue * we prepend lifetime in front of PDU */ struct timeval *tv; @@ -95,6 +129,7 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class, memcpy(msgb_put(llc_msg, len), data, len); m_llc.enqueue(llc_msg); tbf_update_ms_class(this, ms_class); + start_llc_timer(); } return 0; |