aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2018-05-03 16:19:06 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2018-05-15 01:40:26 +0200
commit66fd5f891845fecc606dcc8c8a41e95be2d74ca9 (patch)
tree1d33f6a855d16c971831153b0a6b444d22e027a3
parent9885701760ccbc846f820d089ca75eedac942fe1 (diff)
assignment blahneels/inter_bsc_ho_wip
-rw-r--r--include/osmocom/bsc/gsm_data.h12
-rw-r--r--src/libbsc/bsc_subscr_conn_fsm.c300
2 files changed, 306 insertions, 6 deletions
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 8d2e98c..1577250 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -188,6 +188,18 @@ struct gsm_subscriber_connection {
enum gsm48_chan_mode chan_mode;
} user_plane;
+
+ struct {
+ enum gscon_fsm_states next_state;
+ unsigned int timeout_secs;
+ int T;
+ uint32_t success_event;
+ uint32_t failure_event;
+ enum gsm0808_cause cause;
+ struct gsm_lchan *new_lchan;
+ enum gsm48_chan_mode chan_mode;
+ bool full_rate;
+ } lchan_alloc;
};
diff --git a/src/libbsc/bsc_subscr_conn_fsm.c b/src/libbsc/bsc_subscr_conn_fsm.c
index b638a41..c0c4f4f 100644
--- a/src/libbsc/bsc_subscr_conn_fsm.c
+++ b/src/libbsc/bsc_subscr_conn_fsm.c
@@ -56,6 +56,8 @@ enum gscon_fsm_states {
ST_WAIT_CC,
/* active connection */
ST_ACTIVE,
+ /* when allocating an lchan, it may be necessary to deactivate PDCH on a dyn ts first. */
+ ST_WAIT_DYN_TS_SWITCHOVER,
/* during assignment; waiting for ASS_CMPL */
ST_WAIT_ASS_CMPL,
/* during assignment; waiting for MODE_MODIFY_ACK */
@@ -503,6 +505,297 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
}
+/*
+ * Start a new assignment and make sure that it is completed within T10 either
+ * positively, negatively or by the timeout.
+ *
+ * 1.) allocate a new lchan
+ * 2.) copy the encryption key and other data from the
+ * old to the new channel.
+ * 3.) RSL Channel Activate this channel and wait
+ *
+ * -> Signal handler for the LCHAN
+ * 4.) Send GSM 04.08 assignment command to the MS
+ *
+ * -> Assignment Complete/Assignment Failure
+ * 5.) Release the SDCCH, continue signalling on the new link
+ */
+static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
+{
+ struct gsm_lchan *new_lchan;
+ enum gsm_chan_t chan_type;
+
+ chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
+
+
+ if (!new_lchan) {
+ LOGP(DMSC, LOGL_NOTICE, "No free channel.\n");
+ return -1;
+ }
+
+ /* copy old data to the new channel */
+ memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr));
+ new_lchan->ms_power = conn->lchan->ms_power;
+ new_lchan->bs_power = conn->lchan->bs_power;
+ new_lchan->rqd_ta = conn->lchan->rqd_ta;
+
+ /* copy new data to it */
+ new_lchan->tch_mode = chan_mode;
+ new_lchan->rsl_cmode = (chan_mode == GSM48_CMODE_SIGN) ?
+ RSL_CMOD_SPD_SIGN : RSL_CMOD_SPD_SPEECH;
+
+ /* handle AMR correctly */
+ if (chan_mode == GSM48_CMODE_SPEECH_AMR)
+ handle_mr_config(conn, new_lchan, full_rate);
+
+
+ if (rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTRA_NORM_ASS, 0) < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
+ lchan_free(new_lchan);
+ return -1;
+ }
+
+ /* remember that we have the channel */
+ conn->secondary_lchan = new_lchan;
+ new_lchan->conn = conn;
+
+ rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
+ return 0;
+}
+
+static void allocate_lchan_done(struct osmo_fsm_inst *fi, enum gsm0808_cause cause_or_zero)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ osmo_fsm_inst_state_chg(fi,
+ conn->lchan_alloc.next_state,
+ conn->lchan_alloc.timeout_secs,
+ conn->lchan_alloc.T);
+
+ if (cause_or_zero) {
+ if (conn->lchan_alloc.new_lchan) {
+ lchan_free(conn->lchan_alloc.new_lchan);
+ conn->lchan_alloc.new_lchan = NULL;
+ }
+ osmo_fsm_inst_dispatch(fi, conn->lchan_alloc.failure_event, &cause);
+ } else
+ osmo_fsm_inst_dispatch(fi, conn->lchan_alloc.success_event, NULL);
+}
+
+static void allocate_lchan_activate(struct osmo_fsm_inst *fi)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_lchan *new_lchan = conn->lchan_alloc.new_lchan;
+ enum gsm48_chan_mode;
+
+ if (!new_lchan) {
+ LOGPFSML(fi, LOGL_ERROR, "cannot activate new lchan: it is NULL\n");
+ allocate_lchan_done(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ }
+
+ /* copy old data to the new channel */
+ if (conn->lchan) {
+ memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr));
+ new_lchan->ms_power = conn->lchan->ms_power;
+ new_lchan->bs_power = conn->lchan->bs_power;
+ new_lchan->rqd_ta = conn->lchan->rqd_ta;
+ }
+
+ /* copy new data to it */
+ chan_mode = conn->lchan_alloc.chan_mode;
+ new_lchan->tch_mode = chan_mode;
+ new_lchan->rsl_cmode = (chan_mode == GSM48_CMODE_SIGN) ?
+ RSL_CMOD_SPD_SIGN : RSL_CMOD_SPD_SPEECH;
+
+ /* handle AMR correctly */
+ if (chan_mode == GSM48_CMODE_SPEECH_AMR)
+ handle_mr_config(conn, new_lchan, conn->lchan_alloc.full_rate ? 1 : 0);
+
+ if (rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTRA_NORM_ASS, 0) < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
+ lchan_free(new_lchan);
+ return -1;
+ }
+
+ /* remember that we have the channel */
+ conn->secondary_lchan = new_lchan;
+ new_lchan->conn = conn;
+
+ rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
+
+ allocate_lchan_done(fi, 0);
+}
+
+/* If necessary, release PDCH on dynamic TS. The switch-on of PDCH is handled when a voice call ends, not
+ * here. Return true if a dyn TS is in PDCH mode, i.e. it is still active and we need to release and
+ * wait for the release ack. Return false if the caller can carry on right away.
+ */
+static bool allocate_lchan_disable_dyn_ts_pdch(struct osmo_fsm_inst *fi)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_lchan *lchan = conn->lchan_alloc.new_lchan;
+ bool deactivating_pdch = false;
+ int rc = 0;
+
+ switch (lchan->ts->pchan) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ if (lchan->ts->flags & TS_F_PDCH_ACTIVE) {
+ deactivating_pdch = true;
+ rc = rsl_ipacc_pdch_activate(lchan->ts, 0);
+ }
+ break;
+
+ case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+
+ if (lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
+ deactivating_pdch = true;
+
+ if (lchan->ts->dyn.pchan_is != lchan->ts->dyn.pchan_want) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "%s cannot deactivate PDCH, dyn TS already in transition\n",
+ gsm_lchan_name(lchan));
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = dyn_ts_switchover_start(lchan->ts, pchan_for_lchant(lchan->type));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!deactivating_pdch)
+ return false;
+
+ if (rc) {
+ allocate_lchan_done(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ return true;
+ }
+
+ osmo_fsm_inst_state_chg(fi,
+ ST_WAIT_DYN_TS_SWITCHOVER,
+ conn->lchan_alloc.timeout_secs,
+ conn->lchan_alloc.T);
+}
+
+static void allocate_lchan(struct osmo_fsm_inst *fi,
+ struct gsm_bts *on_bts,
+ enum gsm48_chan_mode chan_mode,
+ bool full_rate,
+ bool differ_from_current_lchan,
+ enum gscon_fsm_states next_state,
+ unsigned int timeout_secs, int T,
+ enum gscon_fsm_event success_event,
+ enum gscon_fsm_event failure_event)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_lchan *new_lchan;
+ enum gsm0808_cause cause = GSM0808_CAUSE_EQUIPMENT_FAILURE;
+ enum gsm_chan_t chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
+
+ if (conn->lchan_alloc.new_lchan) {
+ LOGPFSML(fi, LOGL_ERROR, "lchan allocation already busy on %s, cannot start another\n",
+ gsm_lchan_name(conn->lchan_alloc.new_lchan));
+ return;
+ }
+
+ conn->lchan_alloc.next_state = next_state;
+ conn->lchan_alloc.timeout_secs = timeout_secs;
+ conn->lchan_alloc.T = T;
+ conn->lchan_alloc.success_event = success_event;
+ conn->lchan_alloc.failure_event = failure_event;
+
+ conn->lchan_alloc.chan_mode = chan_mode;
+ conn->lchan_alloc.full_rate = full_rate;
+
+ switch (chan_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ break;
+ default:
+ LOGPFSML(fi, LOGL_ERROR, "this code path is not capable of allocating %s\n",
+ get_value_string(gsm48_chan_mode_names, chan_mode));
+ goto failure;
+ }
+
+ /* About allow_bigger: currently this is only used for TCH, and allow_bigger is about
+ * allocating SDCCH. So just pass 0. */
+ new_lchan = lchan_alloc(on_bts, chan_type, 0);
+
+ if (!new_lchan) {
+ LOGPFSML(fi, LOGL_NOTICE, "No lchan available for %s\n",
+ gsm_lchant_name(chan_type));
+ cause = GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE;
+ goto failure;
+ }
+
+ if (differ_from_current_lchan
+ && conn->lchan
+ && conn->lchan->ts->trx->bts == new_lchan->ts->trx->bts
+ && conn->lchan->type == new_lchan->type) {
+ LOGPFSML(fi, LOGL_NOTICE,
+ "%s -> %s Will not re-assign to identical channel type,"
+ " %s was requested\n",
+ gsm_lchan_name(conn->lchan), gsm_lchan_name(new_lchan),
+ gsm_lchant_name(chan_type));
+ lchan_free(new_lchan);
+ cause = GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE;
+ goto failure;
+ }
+
+ conn->lchan_alloc.new_lchan = new_lchan;
+
+ if (allocate_lchan_disable_dyn_ts_pdch(fi))
+ return;
+
+ allocate_lchan_activate(fi);
+ return;
+
+failure:
+ osmo_fsm_inst_state_chg(fi, next_state, timeout_secs, T);
+ osmo_fsm_inst_dispatch(fi, failure_event, &cause);
+}
+
+static void assignment_request(struct osmo_fsm_inst *fi)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ int chan_mode = conn->user_plane.chan_mode;
+ int full_rate = conn->user_plane.full_rate;
+ enum gsm0808_cause cause = GSM0808_CAUSE_EQUIPMENT_FAILURE;
+
+ if (conn->lchan && chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) {
+ /* Already got a compatible lchan, just modify speech */
+ if (chan_mode == GSM48_CMODE_SPEECH_AMR)
+ handle_mr_config(conn, conn->lchan, full_rate);
+
+ LOGPFSML(fi, LOGL_INFO,
+ "%s: Sending ChanModify for speech: %s\n",
+ gsm_lchan_name(conn->lchan),
+ get_value_string(gsm48_chan_mode_names, chan_mode));
+
+ if (gsm48_lchan_modify(conn->lchan, chan_mode)) {
+ LOGPFSML(fi, LOGL_ERROR, "Sending ChanModify failed\n");
+ goto error;
+ }
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR);
+ osmo_fsm_inst_dispatch(fi, GSCON_EV_RR_ASS_COMPL, NULL);
+ return;
+ }
+
+ /* No (suitable) channel available, allocate */
+ allocate_lchan(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR,
+ GSCON_EV_RR_ASS_FAIL);
+ return;
+
+error:
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR);
+ osmo_fsm_inst_dispatch(fi, GSCON_EV_RR_ASS_FAIL, &cause);
+ return -1;
+}
+
static void gscon_fsm_wait_ho_chan_act(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
@@ -591,13 +884,8 @@ static void gscon_fsm_wait_crcx_bts(struct osmo_fsm_inst *fi, uint32_t event, vo
* then start the channel assignment. */
conn->user_plane.rtp_port = conn_peer->port;
conn->user_plane.rtp_ip = osmo_ntohl(inet_addr(conn_peer->addr));
- rc = gsm0808_assign_req(conn, conn->user_plane.chan_mode, conn->user_plane.full_rate);
- if (rc != 0) {
- assignment_failed(fi, GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE);
- return;
- }
- osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR);
+ allocate_lchan(fi, ST_WAIT_ASS_CMPL);
break;
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);