aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-11-14 16:19:48 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-11-15 20:06:47 +0100
commit77cd95d5b55ac7762d82c6a98b358b82e2ac96a9 (patch)
tree6adf17213f2c29d313d8fef709f65a9a832699ec /openbsc
parentf05750ca24d12e679c8043537997b05b6d3ef1ab (diff)
bsc_api: Implement the assignment command for the BSC.
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/gsm_data.h5
-rw-r--r--openbsc/src/bsc_api.c219
2 files changed, 217 insertions, 7 deletions
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e6c0a2e91..e40b1188a 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -255,6 +255,11 @@ struct gsm_subscriber_connection {
struct gsm_lchan *lchan;
struct gsm_lchan *ho_lchan;
struct gsm_bts *bts;
+
+ /* for assignment handling */
+ struct timer_list T10;
+ struct gsm_lchan *secondary_lchan;
+
};
struct gsm_lchan {
diff --git a/openbsc/src/bsc_api.c b/openbsc/src/bsc_api.c
index 6639dae2f..043006ecb 100644
--- a/openbsc/src/bsc_api.c
+++ b/openbsc/src/bsc_api.c
@@ -30,16 +30,21 @@
#include <openbsc/chan_alloc.h>
#include <openbsc/handover.h>
#include <openbsc/debug.h>
+#include <openbsc/gsm_04_08.h>
#include <osmocore/protocol/gsm_08_08.h>
#include <osmocore/talloc.h>
+#define GSM0808_T10_VALUE 6, 0
+
static LLIST_HEAD(sub_connections);
static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind);
static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id);
static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
+static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
+static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
/* GSM 08.08 3.2.2.33 */
static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
@@ -123,6 +128,81 @@ static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan)
return mode;
}
+static void assignment_t10_timeout(void *_conn)
+{
+ struct bsc_api *api;
+ struct gsm_subscriber_connection *conn =
+ (struct gsm_subscriber_connection *) _conn;
+
+ LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn);
+
+ /* normal release on the secondary channel */
+ lchan_release(conn->secondary_lchan, 0, 1);
+ conn->secondary_lchan = NULL;
+
+ /* inform them about the failure */
+ api = conn->bts->network->bsc_api;
+ api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
+}
+
+/*
+ * 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;
+ int chan_type;
+
+ chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
+
+ new_lchan = lchan_alloc(conn->bts, chan_type, 0);
+
+ 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;
+
+ /* copy new data to it */
+ 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);
+ 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;
+}
struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan)
{
@@ -163,6 +243,11 @@ void subscr_con_free(struct gsm_subscriber_connection *conn)
conn->lchan->conn = NULL;
}
+ if (conn->secondary_lchan) {
+ LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n");
+ conn->secondary_lchan->conn = NULL;
+ }
+
llist_del(&conn->entry);
talloc_free(conn);
}
@@ -213,7 +298,8 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in
api = conn->bts->network->bsc_api;
if (conn->lchan->type == GSM_LCHAN_SDCCH) {
- api->assign_fail(conn, 0, NULL);
+ if (handle_new_assignment(conn, chan_mode, full_rate) != 0)
+ goto error;
} else {
LOGP(DMSC, LOGL_NOTICE,
"Sending ChanModify for speech %d %d\n", chan_mode, full_rate);
@@ -223,10 +309,18 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in
conn->lchan->mr_conf.m5_90 = 1;
}
- return gsm48_lchan_modify(conn->lchan, chan_mode);
+ gsm48_lchan_modify(conn->lchan, chan_mode);
}
+ /* we will now start the timer to complete the assignment */
+ conn->T10.cb = assignment_t10_timeout;
+ conn->T10.data = conn;
+ bsc_schedule_timer(&conn->T10, GSM0808_T10_VALUE);
return 0;
+
+error:
+ api->assign_fail(conn, 0, NULL);
+ return -1;
}
int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len,
@@ -254,6 +348,72 @@ int bsc_upqueue(struct gsm_network *net)
return work;
}
+static void handle_ass_compl(struct gsm_subscriber_connection *conn,
+ struct msgb *msg)
+{
+ struct gsm48_hdr *gh;
+ struct bsc_api *api = conn->bts->network->bsc_api;
+
+ if (conn->secondary_lchan != msg->lchan) {
+ LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n");
+ return;
+ }
+
+ gh = msgb_l3(msg);
+ if (msgb_l3len(msg) - sizeof(*gh) != 1) {
+ LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %d\n",
+ msgb_l3len(msg) - sizeof(*gh));
+ return;
+ }
+
+ /* swap channels */
+ bsc_del_timer(&conn->T10);
+
+ lchan_release(conn->lchan, 0, 1);
+ conn->lchan = conn->secondary_lchan;
+ conn->secondary_lchan = NULL;
+
+ if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN)
+ rsl_ipacc_crcx(conn->lchan);
+
+ api->assign_compl(conn, gh->data[0],
+ lchan_to_chosen_channel(conn->lchan),
+ conn->lchan->encr.alg_id,
+ chan_mode_to_speech(conn->lchan));
+}
+
+static void handle_ass_fail(struct gsm_subscriber_connection *conn,
+ struct msgb *msg)
+{
+ struct bsc_api *api = conn->bts->network->bsc_api;
+ uint8_t *rr_failure;
+ struct gsm48_hdr *gh;
+
+
+ if (conn->lchan != msg->lchan) {
+ LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n");
+ return;
+ }
+
+ /* stop the timer and release it */
+ bsc_del_timer(&conn->T10);
+ lchan_release(conn->secondary_lchan, 0, 1);
+ conn->secondary_lchan = NULL;
+
+ gh = msgb_l3(msg);
+ if (msgb_l3len(msg) - sizeof(*gh) != 1) {
+ LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %d\n",
+ msgb_l3len(msg) - sizeof(*gh));
+ rr_failure = NULL;
+ } else {
+ rr_failure = &gh->data[0];
+ }
+
+ api->assign_fail(conn,
+ GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE,
+ rr_failure);
+}
+
static void dispatch_dtap(struct gsm_subscriber_connection *conn,
uint8_t link_id, struct msgb *msg)
{
@@ -278,12 +438,13 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
conn->lchan->encr.alg_id);
break;
case GSM48_MT_RR_ASS_COMPL:
- LOGP(DMSC, LOGL_ERROR, "Assignment command is not handled.\n");
+ handle_ass_compl(conn, msg);
break;
case GSM48_MT_RR_ASS_FAIL:
- LOGP(DMSC, LOGL_ERROR, "Assignment failure is not handled.\n");
+ handle_ass_fail(conn, msg);
break;
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
+ bsc_del_timer(&conn->T10);
rc = gsm48_rx_rr_modif_ack(msg);
if (rc < 0 && api->assign_fail) {
api->assign_fail(conn,
@@ -369,13 +530,19 @@ int gsm0808_clear(struct gsm_subscriber_connection *conn)
if (conn->ho_lchan)
bsc_clear_handover(conn);
+ if (conn->secondary_lchan)
+ lchan_release(conn->secondary_lchan, 0, 1);
+
if (conn->lchan)
lchan_release(conn->lchan, 1, 0);
conn->lchan = NULL;
+ conn->secondary_lchan = NULL;
conn->ho_lchan = NULL;
conn->bts = NULL;
+ bsc_del_timer(&conn->T10);
+
return 0;
}
@@ -439,6 +606,12 @@ static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal,
case S_LCHAN_UNEXPECTED_RELEASE:
handle_release(lchan->conn, bsc, lchan);
break;
+ case S_LCHAN_ACTIVATE_ACK:
+ handle_chan_ack(lchan->conn, bsc, lchan);
+ break;
+ case S_LCHAN_ACTIVATE_NACK:
+ handle_chan_nack(lchan->conn, bsc, lchan);
+ break;
}
return 0;
@@ -449,22 +622,54 @@ static void handle_release(struct gsm_subscriber_connection *conn,
{
int destruct = 1;
- if (bsc->clear_request)
- destruct = bsc->clear_request(conn, 0);
-
/* now give up all channels */
if (conn->lchan == lchan)
conn->lchan = NULL;
if (conn->ho_lchan == lchan)
conn->ho_lchan = NULL;
+ if (conn->secondary_lchan == lchan) {
+ bsc_del_timer(&conn->T10);
+ conn->secondary_lchan = NULL;
+
+ bsc->assign_fail(conn,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE,
+ NULL);
+ }
+
lchan->conn = NULL;
+ /* clear the connection now */
+ if (bsc->clear_request)
+ destruct = bsc->clear_request(conn, 0);
+
+
gsm0808_clear(conn);
if (destruct)
subscr_con_free(conn);
}
+static void handle_chan_ack(struct gsm_subscriber_connection *conn,
+ struct bsc_api *api, struct gsm_lchan *lchan)
+{
+ if (conn->secondary_lchan != lchan)
+ return;
+
+ LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan);
+ gsm48_send_rr_ass_cmd(conn->lchan, lchan, 0x3);
+}
+
+static void handle_chan_nack(struct gsm_subscriber_connection *conn,
+ struct bsc_api *api, struct gsm_lchan *lchan)
+{
+ if (conn->secondary_lchan != lchan)
+ return;
+
+ LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n");
+ conn->secondary_lchan->conn = NULL;
+ conn->secondary_lchan = NULL;
+}
+
static __attribute__((constructor)) void on_dso_load_bsc(void)
{
register_signal_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL);