aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/bsc_api.h1
-rw-r--r--openbsc/include/openbsc/gsm_data.h2
-rw-r--r--openbsc/src/libbsc/bsc_api.c19
-rw-r--r--openbsc/src/libbsc/handover_logic.c35
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c217
5 files changed, 238 insertions, 36 deletions
diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h
index 3a9311991..baacbeda3 100644
--- a/openbsc/include/openbsc/bsc_api.h
+++ b/openbsc/include/openbsc/bsc_api.h
@@ -51,5 +51,6 @@ int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
int gsm0808_page(struct gsm_bts *bts, unsigned int page_group,
unsigned int mi_len, uint8_t *mi, int chan_type);
int gsm0808_clear(struct gsm_subscriber_connection *conn);
+int gsm0808_ho_clear(struct gsm_subscriber_connection *conn);
#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index ac573c49d..542b26113 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -138,6 +138,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 8a4c85ff3..71e82d033 100644
--- a/openbsc/src/libbsc/bsc_api.c
+++ b/openbsc/src/libbsc/bsc_api.c
@@ -253,11 +253,14 @@ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lcha
conn->bts = lchan->ts->trx->bts;
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;
@@ -283,6 +286,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);
}
@@ -747,6 +755,17 @@ int gsm0808_clear(struct gsm_subscriber_connection *conn)
return 0;
}
+/*
+ * Release handover RF Channel.
+ */
+int gsm0808_ho_clear(struct gsm_subscriber_connection *conn)
+{
+ if (conn->ho_lchan)
+ bsc_clear_handover(conn, 1);
+
+ return 0;
+}
+
static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id)
{
struct bsc_api *api;
diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c
index af4e8013f..b7085c349 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,6 +221,8 @@ 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 */
ho->T3103.cb = ho_T3103_cb;
@@ -221,7 +230,8 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
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;
@@ -273,6 +283,11 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
if (is_e1_bts(new_lchan->conn->bts))
switch_trau_mux(ho->old_lchan, new_lchan);
+ if (ho->old_lchan->conn->mncc_rtp_connect_pending) {
+ new_lchan->abis_ip.connect_port = ho->old_lchan->abis_ip.connect_port;
+ new_lchan->abis_ip.connect_ip = ho->old_lchan->abis_ip.connect_ip;
+ }
+
/* Replace the ho lchan with the primary one */
if (ho->old_lchan != new_lchan->conn->lchan)
LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n");
@@ -295,27 +310,9 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
{
struct gsm_network *net = old_lchan->ts->trx->bts->network;
- struct bsc_handover *ho;
- struct gsm_lchan *new_lchan;
-
- ho = bsc_ho_by_old_lchan(old_lchan);
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
- return -ENODEV;
- }
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
- new_lchan = ho->new_lchan;
-
- /* release the channel and forget about it */
- ho->new_lchan->conn->ho_lchan = NULL;
- ho->new_lchan->conn = NULL;
- handover_free(ho);
-
- lchan_release(new_lchan, 0, RSL_REL_LOCAL_END);
-
-
return 0;
}
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index e5402d0a6..84338d721 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -147,6 +147,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);
@@ -1749,11 +1758,8 @@ static int switch_for_handover(struct gsm_lchan *old_lchan,
struct rtp_socket *old_rs, *new_rs, *other_rs;
/* Ask the new socket to send to the already known port. */
- if (new_lchan->conn->mncc_rtp_bridge) {
- LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n");
- rsl_ipacc_mdcx(new_lchan,
- old_lchan->abis_ip.connect_ip,
- old_lchan->abis_ip.connect_port, 0);
+ if (new_lchan->ts->trx->bts->network->mncc_state) {
+ /* Audio path should be switched after receiving ho detect message.*/
return 0;
}
@@ -1821,8 +1827,10 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (subsys != SS_ABISIP)
return 0;
+ net = lchan->ts->trx->bts->network;
+
/* RTP bridge handling */
- if (lchan->conn && lchan->conn->mncc_rtp_bridge)
+ if (lchan->conn && net->mncc_state)
return tch_rtp_signal(lchan, signal);
/* in case we use direct BTS-to-BTS RTP */
@@ -1851,7 +1859,6 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
/* check if any transactions on this lchan still have
* a tch_recv_mncc request pending */
- net = lchan->ts->trx->bts->network;
llist_for_each_entry(trans, &net->trans_list, entry) {
if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) {
DEBUGP(DCC, "pending tch_recv_mncc request\n");
@@ -2017,6 +2024,13 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable)
LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n");
return -EINVAL;
}
+ /* RTP bridge handling */
+ /* TODO: this is from fairwaves/master-rebase branch. It's not entirely clear which case
+ * this is catching and why. Find out and explain. */
+ if (lchan->conn && net->mncc_state) {
+ LOGP(DCC, LOGL_NOTICE, "XXXXXXXXXX tch_recv_mncc(): if (lchan->conn && net->mncc_state) return 0\n");
+ return 0;
+ }
/* In case, we don't have a RTP socket to the BTS yet, the BTS
* will not be connected to our RTP proxy and the socket will
* not be assigned to the application interface. This method
@@ -3325,6 +3339,41 @@ static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd
return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
}
+static void mncc_recv_rtp_modify(struct gsm_lchan *lchan, uint32_t callref)
+{
+ int msg_type;
+ struct gsm_network *net = lchan->ts->trx->bts->network;
+
+ LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP modify ind.\n",
+ gsm_lchan_name(lchan));
+
+ switch (lchan->abis_ip.rtp_payload) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ break;
+ case RTP_PT_GSM_EFR:
+ msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ case RTP_PT_GSM_HALF:
+ msg_type = GSM_TCHH_FRAME;
+ break;
+ case RTP_PT_AMR:
+ msg_type = GSM_TCH_FRAME_AMR;
+ break;
+ default:
+ LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n",
+ gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload);
+ msg_type = 0;
+ break;
+ }
+
+ mncc_recv_rtp(net, callref, MNCC_RTP_MODIFY,
+ lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload,
+ msg_type);
+}
+
static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
{
struct gsm_bts *bts;
@@ -3374,6 +3423,9 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
LOGP(DMNCC, LOGL_DEBUG, "RTP create: codec=%s, chan_type=%s\n",
get_value_string(gsm48_chan_mode_names, m),
get_value_string(gsm_chan_t_names, lchan->type));
+ if (trans->conn->in_handover) {
+ return 0;
+ }
return gsm0808_assign_req(trans->conn, m,
lchan->type != GSM_LCHAN_TCH_H);
}
@@ -3420,23 +3472,21 @@ static int tch_rtp_connect(struct gsm_network *net, void *arg)
* same package!
*/
trans->conn->mncc_rtp_connect_pending = 1;
+ if (trans->conn->in_handover) {
+ lchan->abis_ip.connect_port = rtp->port;
+ lchan->abis_ip.connect_ip = rtp->ip;
+ return 0;
+ }
return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
}
static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
{
struct gsm_network *net;
- struct gsm_trans *tmp, *trans = NULL;
+ struct gsm_trans *trans;
net = lchan->ts->trx->bts->network;
- llist_for_each_entry(tmp, &net->trans_list, entry) {
- if (!tmp->conn)
- continue;
- if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan)
- continue;
- trans = tmp;
- break;
- }
+ trans = trans_find_by_lchan(lchan);
if (!trans) {
LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n",
@@ -3459,7 +3509,7 @@ static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
maybe_switch_for_handover(lchan);
break;
case S_ABISIP_MDCX_ACK:
- if (lchan->conn->mncc_rtp_connect_pending) {
+ if (lchan->conn->mncc_rtp_connect_pending && !lchan->conn->in_handover) {
lchan->conn->mncc_rtp_connect_pending = 0;
LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n",
gsm_lchan_name(lchan));
@@ -3471,6 +3521,134 @@ static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
return 0;
}
+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;
+ enum gsm48_chan_mode m;
+
+ while (!llist_empty(&lchan->conn->ho_queue)) {
+ msg = msgb_dequeue(&lchan->conn->ho_queue);
+ gsm48_conn_sendmsg(msg, lchan->conn, trans);
+ }
+
+ if (trans->conn->mncc_rtp_create_pending &&
+ lchan->tch_mode == GSM48_CMODE_SIGN) {
+ m = mncc_codec_for_mode(lchan->type);
+ gsm0808_assign_req(lchan->conn, m, lchan->type != GSM_LCHAN_TCH_H);
+ }
+
+ if (trans->conn->mncc_rtp_connect_pending) {
+ rsl_ipacc_mdcx(lchan, lchan->abis_ip.connect_ip, lchan->abis_ip.connect_port, 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;
+}
+
+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);
+ }
+
+ gsm0808_ho_clear(old_lchan->conn);
+ return 0;
+}
+
+static int ho_detect(struct gsm_lchan *new_lchan)
+{
+ struct gsm_trans *trans;
+ struct gsm_lchan *old_lchan;
+
+ trans = trans_find_by_lchan(new_lchan);
+
+ if (!trans) {
+ LOGP(DHO, LOGL_ERROR, "%s HO detected, but no transaction for new_lchan"
+ " with enabled tch_recv.\n",
+ gsm_lchan_name(new_lchan));
+ return 0;
+ }
+
+ if (!new_lchan->conn->mncc_rtp_bridge) {
+ LOGP(DHO, LOGL_ERROR, "%s HO detected, but connection not in mncc_rtp_bridge mode.\n",
+ gsm_lchan_name(new_lchan));
+ return 0;
+ }
+
+ old_lchan = bsc_handover_pending(new_lchan);
+ if (!old_lchan) {
+ LOGP(DHO, LOGL_ERROR, "%s HO detected, but no old_lchan for handover.\n",
+ gsm_lchan_name(new_lchan));
+ return 0;
+ }
+
+ LOGP(DHO, LOGL_DEBUG, "HO detected, forwarding RTP\n");
+ rsl_ipacc_mdcx(new_lchan,
+ old_lchan->abis_ip.connect_ip,
+ old_lchan->abis_ip.connect_port, 0);
+
+ mncc_recv_rtp_modify(new_lchan, trans->callref);
+
+ 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)
+{
+ struct lchan_signal_data *lchan_data;
+ struct gsm_lchan *lchan;
+
+ lchan_data = signal_data;
+ switch (subsys) {
+ case SS_LCHAN:
+ lchan = lchan_data->lchan;
+ if (!lchan->conn)
+ return 0;
+ 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;
+ }
+
+ return 0;
+}
static struct downstate {
uint32_t states;
@@ -3853,6 +4031,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, "