summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/gsm_data.h2
-rw-r--r--openbsc/src/libbsc/bsc_api.c8
-rw-r--r--openbsc/src/libbsc/handover_logic.c12
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c71
4 files changed, 92 insertions, 1 deletions
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 57fa30173..8d5fcfff0 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -157,6 +157,8 @@ struct gsm_subscriber_connection {
struct gsm_network *network;
int in_release;
+ int in_handover;
+ struct llist_head ho_queue;
struct gsm_lchan *lchan; /* BSC */
struct gsm_lchan *ho_lchan; /* BSC */
struct gsm_bts *bts; /* BSC */
diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c
index 0c16db303..01fe589af 100644
--- a/openbsc/src/libbsc/bsc_api.c
+++ b/openbsc/src/libbsc/bsc_api.c
@@ -265,11 +265,14 @@ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lcha
conn->via_ran = RAN_GERAN_A;
lchan->conn = conn;
llist_add_tail(&conn->entry, &net->subscr_conns);
+ INIT_LLIST_HEAD(&conn->ho_queue);
return conn;
}
void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
{
+ struct msgb *msg;
+
if (!conn)
return;
@@ -295,6 +298,11 @@ void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
conn->secondary_lchan->conn = NULL;
}
+ while (!llist_empty(&conn->ho_queue)) {
+ msg = msgb_dequeue(&conn->ho_queue);
+ msgb_free(msg);
+ }
+
llist_del(&conn->entry);
talloc_free(conn);
}
diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c
index ceaf62abc..ebe9d44d2 100644
--- a/openbsc/src/libbsc/handover_logic.c
+++ b/openbsc/src/libbsc/handover_logic.c
@@ -186,10 +186,17 @@ static void ho_T3103_cb(void *_ho)
{
struct bsc_handover *ho = _ho;
struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
+ struct msgb *msg;
DEBUGP(DHO, "HO T3103 expired\n");
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]);
+ ho->new_lchan->conn->in_handover = 0;
+ while (!llist_empty(&ho->new_lchan->conn->ho_queue)) {
+ msg = msgb_dequeue(&ho->new_lchan->conn->ho_queue);
+ msgb_free(msg);
+ }
+
ho->new_lchan->conn->ho_lchan = NULL;
ho->new_lchan->conn = NULL;
lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
@@ -214,13 +221,16 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
+ new_lchan->conn->in_handover = 1;
+
/* start T3103. We can continue either with T3103 expiration,
* 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho);
osmo_timer_schedule(&ho->T3103, 10, 0);
/* create a RTP connection */
- if (is_ipaccess_bts(new_lchan->ts->trx->bts))
+ if (is_ipaccess_bts(new_lchan->ts->trx->bts) &&
+ new_lchan->tch_mode != GSM48_CMODE_SIGN)
rsl_ipacc_crcx(new_lchan);
return 0;
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index d34f64e4d..8fc4c1a53 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -148,6 +148,15 @@ static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection
sign_link->trx->bts->nr,
sign_link->trx->nr, msg->lchan->ts->nr,
gh->proto_discr, gh->msg_type);
+
+ if (conn->in_handover) {
+ msgb_enqueue(&conn->ho_queue, msg);
+ DEBUGP(DCC, "(bts %d trx %d ts %d) Suspend message sending to MS, "
+ "active HO procedure.\n",
+ sign_link->trx->bts->nr,
+ sign_link->trx->nr, msg->lchan->ts->nr);
+ return 0;
+ }
}
return gsm0808_submit_dtap(conn, msg, 0, 0);
@@ -3811,6 +3820,11 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m
gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
gsm48_cc_state_name(trans?(trans->cc.state):0));
+ if (conn->in_handover) {
+ DEBUGP(DCC, "Message unhandled, handover procedure.\n");
+ return 0;
+ }
+
/* Create transaction */
if (!trans) {
DEBUGP(DCC, "Unknown transaction ID %x, "
@@ -4035,6 +4049,59 @@ static int ho_detect(struct gsm_lchan *new_lchan)
return ho_switch_audio_rtp_proxy(old_lchan, new_lchan);
}
+static void ho_queue_clean(struct gsm_subscriber_connection *conn)
+{
+ struct msgb *msg;
+ while (!llist_empty(&conn->ho_queue)) {
+ msg = msgb_dequeue(&conn->ho_queue);
+ msgb_free(msg);
+ }
+}
+
+static void ho_resumption(struct gsm_lchan *lchan, struct gsm_trans *trans)
+{
+ struct msgb *msg;
+
+ while (!llist_empty(&lchan->conn->ho_queue)) {
+ msg = msgb_dequeue(&lchan->conn->ho_queue);
+ gsm48_conn_sendmsg(msg, lchan->conn, trans);
+ }
+}
+
+static int ho_fail(struct gsm_lchan *old_lchan)
+{
+ struct gsm_trans *trans;
+
+ old_lchan->conn->in_handover = 0;
+ trans = trans_find_by_lchan(old_lchan);
+ if (trans)
+ ho_resumption(old_lchan, trans);
+ else {
+ LOGP(DHO, LOGL_ERROR, "%s HO fail, but no transaction for old_lchan.\n",
+ gsm_lchan_name(old_lchan));
+ ho_queue_clean(old_lchan->conn);
+ }
+
+ return 0;
+}
+
+static int ho_complete(struct gsm_lchan *new_lchan)
+{
+ struct gsm_trans *trans;
+
+ new_lchan->conn->in_handover = 0;
+ trans = trans_find_by_lchan(new_lchan);
+ if (!trans) {
+ LOGP(DHO, LOGL_ERROR, "%s HO detected, but no transaction for new_lchan.\n",
+ gsm_lchan_name(new_lchan));
+ ho_queue_clean(new_lchan->conn);
+ return 0;
+ }
+
+ ho_resumption(new_lchan, trans);
+ return 0;
+}
+
/* some other part of the code sends us a signal */
static int handle_lchan_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
@@ -4049,6 +4116,10 @@ static int handle_lchan_signal(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_LCHAN_HANDOVER_DETECT:
return ho_detect(lchan);
+ case S_LCHAN_HANDOVER_COMPL:
+ return ho_complete(lchan);
+ case S_LCHAN_HANDOVER_FAIL:
+ return ho_fail(lchan);
}
break;
}