bsc_api: Implement the assignment command for the BSC.
This commit is contained in:
parent
f05750ca24
commit
77cd95d5b5
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue