aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bts.h1
-rw-r--r--src/pcu_vty.c34
-rw-r--r--src/tbf.cpp1
-rw-r--r--src/tbf.h5
-rw-r--r--src/tbf_dl.cpp35
5 files changed, 75 insertions, 1 deletions
diff --git a/src/bts.h b/src/bts.h
index fae3c5e5..4b7f7e86 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -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));
diff --git a/src/tbf.h b/src/tbf.h
index 17ba980e..e835ab6f 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -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;