mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	bsc_api: Implement the assignment command for the BSC.
This commit is contained in:
		@@ -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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user