diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-11-14 16:19:48 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-11-15 20:06:47 +0100 |
commit | 77cd95d5b55ac7762d82c6a98b358b82e2ac96a9 (patch) | |
tree | 6adf17213f2c29d313d8fef709f65a9a832699ec /openbsc | |
parent | f05750ca24d12e679c8043537997b05b6d3ef1ab (diff) |
bsc_api: Implement the assignment command for the BSC.
Diffstat (limited to 'openbsc')
-rw-r--r-- | openbsc/include/openbsc/gsm_data.h | 5 | ||||
-rw-r--r-- | openbsc/src/bsc_api.c | 219 |
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); |