aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-01-28 10:04:13 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-01-28 11:59:51 +0100
commit556008d724c8b5062a02e4ac1ab6c9e62e02cc33 (patch)
tree82d58dc55825c10aa99281ce7de5ba990a76253c
parent0094f84f305bcdbe29478170109639de9737f088 (diff)
[bsc] Implement early assignment for CC for the MT case.
In case we need to handle speech but we are currently on a SDCCH we need to assign a new channel and close the old one. This implementation should have the correct flow of things but we might need to fix some error situations properly. It is implemented by keeping a secondary_lchan pointer that will be swapped into the lchan pointer after the assignment complete message from the MS. The old lchan will be deactivated (the SACCH should stay open). We have to manually remove the subscr from the lchan structure to properly close things down.
-rw-r--r--openbsc/include/openbsc/gsm_data.h1
-rw-r--r--openbsc/src/bsc_msc_ip.c33
-rw-r--r--openbsc/src/bssap.c124
3 files changed, 149 insertions, 9 deletions
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 486067008..b1abf6fdd 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -107,6 +107,7 @@ struct rtp_socket;
/* BSC/MSC data holding them together */
struct bss_sccp_connection_data {
struct gsm_lchan *lchan;
+ struct gsm_lchan *secondary_lchan;
struct sccp_connection *sccp;
int ciphering_handled : 1;
diff --git a/openbsc/src/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c
index 33bdf50c0..54ed96b2a 100644
--- a/openbsc/src/bsc_msc_ip.c
+++ b/openbsc/src/bsc_msc_ip.c
@@ -298,20 +298,45 @@ static int handle_cipher_m_complete(struct msgb *msg)
/* Receive a ASSIGNMENT COMPLETE */
static int handle_ass_compl(struct msgb *msg)
{
+ struct gsm_lchan *old_chan;
struct gsm48_hdr *gh = msgb_l3(msg);
DEBUGP(DMSC, "ASSIGNMENT COMPLETE from MS, forwarding to MSC\n");
if (!msg->lchan->msc_data) {
DEBUGP(DMSC, "No MSC data\n");
+ put_lchan(msg->lchan);
+ return -1;
+ }
+
+ if (msg->lchan->msc_data->secondary_lchan != msg->lchan) {
+ LOGP(DMSC, LOGL_NOTICE, "Wrong assignment complete.\n");
+ put_lchan(msg->lchan);
return -1;
}
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
DEBUGP(DMSC, "assignment failure invalid: %d\n",
msgb_l3len(msg) - sizeof(*gh));
+ put_lchan(msg->lchan);
return -1;
}
+
+ /* swap the channels and release the old */
+ old_chan = msg->lchan->msc_data->lchan;
+ msg->lchan->msc_data->lchan = msg->lchan;
+ msg->lchan->msc_data->secondary_lchan = NULL;
+ old_chan->msc_data = NULL;
+
+ /* give up the old channel to not do a SACCH deactivate */
+ subscr_put(old_chan->subscr);
+ old_chan->subscr = NULL;
+ put_lchan(old_chan);
+
+ /* activate audio on it... */
+ if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && msg->lchan->tch_mode != GSM48_CMODE_SIGN)
+ rsl_ipacc_crcx(msg->lchan);
+
gsm0808_send_assignment_compl(msg->lchan, gh->data[0]);
return 1;
}
@@ -327,12 +352,20 @@ static int handle_ass_fail(struct msgb *msg)
DEBUGP(DMSC, "ASSIGNMENT FAILURE from MS, forwarding to MSC\n");
if (!msg->lchan->msc_data) {
DEBUGP(DMSC, "No MSC data\n");
+ put_lchan(msg->lchan);
+ return -1;
+ }
+
+ if (msg->lchan->msc_data->secondary_lchan != msg->lchan) {
+ LOGP(DMSC, LOGL_NOTICE, "Wrong assignment complete.\n");
+ put_lchan(msg->lchan);
return -1;
}
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
DEBUGP(DMSC, "assignment failure invalid: %d\n",
msgb_l3len(msg) - sizeof(*gh));
+ put_lchan(msg->lchan);
return -1;
}
diff --git a/openbsc/src/bssap.c b/openbsc/src/bssap.c
index a6a111e01..3a966f23f 100644
--- a/openbsc/src/bssap.c
+++ b/openbsc/src/bssap.c
@@ -182,6 +182,11 @@ static int bssmap_handle_clear_command(struct sccp_connection *conn,
DEBUGP(DMSC, "Releasing all transactions on %p\n", conn);
bsc_del_timer(&msg->lchan->msc_data->T10);
msg->lchan->msc_data->lchan = NULL;
+
+ /* we might got killed during an assignment */
+ if (msg->lchan->msc_data->secondary_lchan)
+ put_lchan(msg->lchan->msc_data->secondary_lchan);
+
msg->lchan->msc_data = NULL;
put_lchan(msg->lchan);
}
@@ -360,6 +365,88 @@ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
}
/*
+ * The assignment request has started T10. We need to be faster than this
+ * or an assignment failure will be sent...
+ *
+ * 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
+ * 5.) Release the SDCCH, continue signalling on the new link
+ */
+static int handle_new_assignment(struct msgb *msg, int full_rate, int chan_mode)
+{
+ struct bss_sccp_connection_data *msc_data;
+ struct gsm_bts *bts;
+ struct gsm_lchan *new_lchan;
+ int chan_type;
+
+ msc_data = msg->lchan->msc_data;
+ bts = msg->lchan->ts->trx->bts;
+ chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
+
+ new_lchan = lchan_alloc(bts, chan_type);
+
+ if (!new_lchan) {
+ LOGP(DMSC, LOGL_NOTICE, "No free channel.\n");
+ return -1;
+ }
+
+ /* copy old data to the new channel */
+ memcpy(&new_lchan->encr, &msg->lchan->encr, sizeof(new_lchan->encr));
+ new_lchan->ms_power = msg->lchan->ms_power;
+ new_lchan->bs_power = msg->lchan->bs_power;
+ new_lchan->subscr = subscr_get(msg->lchan->subscr);
+
+ /* copy new data to it */
+ use_lchan(new_lchan);
+ new_lchan->tch_mode = chan_mode;
+ new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
+
+ /* handle AMR correctly */
+ if (chan_mode == GSM48_CMODE_SPEECH_AMR) {
+ new_lchan->mr_conf.ver = 1;
+ new_lchan->mr_conf.icmi = 1;
+ new_lchan->mr_conf.m5_90 = 1;
+ }
+
+ if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
+ lchan_free(new_lchan);
+ }
+
+ msc_data->secondary_lchan = new_lchan;
+ new_lchan->msc_data = msc_data;
+ return 0;
+}
+
+/*
+ * Any failure will be caught with the T10 timer ticking...
+ */
+static void continue_new_assignment(struct gsm_lchan *new_lchan)
+{
+ if (!new_lchan->msc_data) {
+ LOGP(DMSC, LOGL_ERROR, "No BSS data found.\n");
+ put_lchan(new_lchan);
+ return;
+ }
+
+ if (new_lchan->msc_data->secondary_lchan != new_lchan) {
+ LOGP(DMSC, LOGL_ERROR, "This is not the secondary channel?\n");
+ put_lchan(new_lchan);
+ return;
+ }
+
+ LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: 0x%p\n", new_lchan);
+ gsm48_send_rr_ass_cmd(new_lchan->msc_data->lchan, new_lchan, 0x3);
+}
+
+/*
* Handle the assignment request message.
*
* See ยง3.2.1.1 for the message type
@@ -375,7 +462,7 @@ static int bssmap_handle_assignm_req(struct sccp_connection *conn,
u_int8_t timeslot;
u_int8_t multiplex;
enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
- int i, supported, port;
+ int i, supported, port, full_rate = -1;
if (!msg->lchan || !msg->lchan->msc_data) {
DEBUGP(DMSC, "No lchan/msc_data in cipher mode command.\n");
@@ -434,6 +521,7 @@ static int bssmap_handle_assignm_req(struct sccp_connection *conn,
* inner loop. The outer loop will exit due chan_mode having
* the correct value.
*/
+ full_rate = 0;
for (supported = 0;
chan_mode == GSM48_CMODE_SIGN && supported < network->audio_length;
++supported) {
@@ -442,6 +530,7 @@ static int bssmap_handle_assignm_req(struct sccp_connection *conn,
for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
if ((data[i] & 0x7f) == perm_val) {
chan_mode = gsm88_to_chan_mode(perm_val);
+ full_rate = (data[i] & 0x4) == 0;
break;
} else if ((data[i] & 0x80) == 0x00) {
break;
@@ -465,15 +554,25 @@ static int bssmap_handle_assignm_req(struct sccp_connection *conn,
port = timeslot + (31 * multiplex);
msc_data->rtp_port = rtp_calculate_port(port,
network->rtp_base_port);
- DEBUGP(DMSC, "Sending ChanModify for speech on: sccp: %p mode: 0x%x on port %d %d/0x%x port: %u\n",
- conn, chan_mode, port, multiplex, timeslot, msc_data->rtp_port);
- if (chan_mode == GSM48_CMODE_SPEECH_AMR) {
- msg->lchan->mr_conf.ver = 1;
- msg->lchan->mr_conf.icmi = 1;
- msg->lchan->mr_conf.m5_90 = 1;
- }
- return gsm48_lchan_modify(msg->lchan, chan_mode);
+ if (msg->lchan->type == GSM_LCHAN_SDCCH) {
+ /* start to assign a new channel, if it works */
+ if (handle_new_assignment(msg, full_rate, chan_mode) == 0)
+ return 0;
+ else
+ goto reject;
+ } else {
+ DEBUGP(DMSC, "Sending ChanModify for speech on: sccp: %p mode: 0x%x on port %d %d/0x%x port: %u\n",
+ conn, chan_mode, port, multiplex, timeslot, msc_data->rtp_port);
+
+ if (chan_mode == GSM48_CMODE_SPEECH_AMR) {
+ msg->lchan->mr_conf.ver = 1;
+ msg->lchan->mr_conf.icmi = 1;
+ msg->lchan->mr_conf.m5_90 = 1;
+ }
+
+ return gsm48_lchan_modify(msg->lchan, chan_mode);
+ }
reject:
gsm0808_send_assignment_failure(msg->lchan,
@@ -952,6 +1051,10 @@ static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal,
return 0;
switch (signal) {
case S_LCHAN_UNEXPECTED_RELEASE:
+ /* handle this through the T10 timeout */
+ if (lchan->msc_data->lchan != lchan)
+ return 0;
+
bsc_del_timer(&lchan->msc_data->T10);
conn = lchan->msc_data->sccp;
lchan->msc_data->lchan = NULL;
@@ -975,6 +1078,9 @@ static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal,
DEBUGP(DMSC, "Sending clear request on unexpected channel release.\n");
bsc_queue_connection_write(conn, msg);
break;
+ case S_LCHAN_ACTIVATE_ACK:
+ continue_new_assignment(lchan);
+ break;
}
break;
}