bsc_api: Implement the assignment command for the BSC.

This commit is contained in:
Holger Hans Peter Freyther 2010-11-14 16:19:48 +01:00
parent f05750ca24
commit 77cd95d5b5
2 changed files with 217 additions and 7 deletions

View File

@ -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 {

View File

@ -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);