summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/mobile/gsm48_rr.c
diff options
context:
space:
mode:
authorAndreas.Eversberg <jolly@eversberg.eu>2010-08-21 15:10:30 +0000
committerAndreas.Eversberg <jolly@eversberg.eu>2010-08-21 15:10:30 +0000
commitc6a423392efd2a2264f9361d782146bdedca2633 (patch)
tree68773f4a06196a96efee1011bfb8213ec49cff8a /src/host/layer23/src/mobile/gsm48_rr.c
parentda65f025807e2af7da420ad33f09b4e11a5b9020 (diff)
[layer23] Radio ressource protocol completed except for handover, meas.
Assignment command is now complete as well as frequency redifinition. The handover process is partly complete. Further functionality depends on layer1 capabilites. The measurement report is also incomplete.
Diffstat (limited to 'src/host/layer23/src/mobile/gsm48_rr.c')
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c1031
1 files changed, 837 insertions, 194 deletions
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index 0e4c2dea..5c39d778 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -1,4 +1,3 @@
-#warning rr on MDL error handling (as specified in 04.08 / 04.06)
/*
* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
*
@@ -41,6 +40,25 @@
*
*/
+/* Testing delayed (immediate) assigment / handover
+ *
+ * When enabled, the starting time will be set by given frames in the future.
+ * If a starting time is given by the network, this time is ignored.
+ */
+//#define TEST_STARTING_TIMER 40
+
+/* Testing if frequency modification works correctly "after time".
+ *
+ * When enabled, the starting time will be set in the future.
+ * A wrong channel is defined "before time", so noise is received until
+ * starting time elapses.
+ * If a starting time is given by the network, this time is ignored.
+ * Also channel definitions "before time" are ignored.
+ *
+ * NOTE: TEST_STARTING_TIMER MUST be defined also.
+ */
+//#define TEST_FREQUENCY_MOD
+
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
@@ -63,6 +81,8 @@
static void start_rr_t_monitor(struct gsm48_rrlayer *rr, int sec, int micro);
static void stop_rr_t_monitor(struct gsm48_rrlayer *rr);
+static void stop_rr_t_starting(struct gsm48_rrlayer *rr);
+static void stop_rr_t3124(struct gsm48_rrlayer *rr);
static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_rr_dl_est(struct osmocom_ms *ms);
@@ -181,6 +201,28 @@ static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
return 0;
}
+/* decode "Cell Description" (10.5.2.2) */
+static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn,
+ uint8_t *ncc, uint8_t *bcc)
+{
+ *arfcn = (cd->arfcn_hi << 8) + cd->arfcn_lo;
+ *ncc = cd->ncc;
+ *bcc = cd->bcc;
+
+ return 0;
+}
+
+/* decode "Synchronization Indication" (10.5.2.39) */
+static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr,
+ struct gsm48_sync_ind *si)
+{
+ rr->hando_sync_ind = si->si;
+ rr->hando_rot = si->rot;
+ rr->hando_nci = si->nci;
+
+ return 0;
+}
+
/* 3.1.4.3 set sequence number and increment */
static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
{
@@ -252,9 +294,13 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
/* abort handover, in case of release of dedicated mode */
- if (rr->state == GSM48_RR_ST_DEDICATED && state != rr->state) {
- rr->hando_susp_state = 0;
- rr->assign_susp_state = 0;
+ if (rr->state == GSM48_RR_ST_DEDICATED) {
+ /* disable handover / assign state */
+ rr->modify_state = GSM48_RR_MOD_NONE;
+ /* stop start_time_timer */
+ stop_rr_t_starting(rr);
+ /* stop handover timer */
+ stop_rr_t3124(rr);
}
rr->state = state;
@@ -277,7 +323,7 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
msgb_free(msg);
/* clear all descriptions of last channel */
memset(&rr->cd_now, 0, sizeof(rr->cd_now));
- /* reset cipering */
+ /* reset ciphering */
rr->cipher_on = 0;
/* tell cell selection process to return to idle mode
* NOTE: this must be sent unbuffered, because it will
@@ -476,7 +522,7 @@ static void timeout_rr_monitor(void *arg)
rxlev = meas->rxlev / meas->frames;
berr = meas->berr / meas->frames;
snr = meas->snr / meas->frames;
- sprintf(text, "MON: arfcn=%d lev=%s snr=%2d ber=%2d "
+ sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d "
"LAI=%s %s %04x ID=%04x", cs->sel_arfcn,
gsm_print_rxlev(rxlev), berr, snr,
gsm_print_mcc(cs->sel_mcc),
@@ -499,6 +545,29 @@ static void timeout_rr_monitor(void *arg)
start_rr_t_monitor(rr, 1, 0);
}
+/* special timer to assign / handover when starting time is reached */
+static void timeout_rr_t_starting(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct msgb *nmsg;
+
+ LOGP(DRR, LOGL_INFO, "starting timer has fired\n");
+
+ /* open channel when starting timer of IMM.ASS has fired */
+ if (rr->modify_state == GSM48_RR_MOD_IMM_ASS) {
+ rr->modify_state = GSM48_RR_MOD_NONE;
+ gsm48_rr_dl_est(rr->ms);
+ return;
+ }
+
+ /* start suspension of current link */
+ LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return;
+ gsm48_send_rsl(rr->ms, RSL_MT_SUSP_REQ, nmsg);
+}
+
/* special timer to ensure that UA is sent before disconnecting channel */
static void timeout_rr_t_rel_wait(void *arg)
{
@@ -539,6 +608,11 @@ static void timeout_rr_t3122(void *arg)
LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
}
+static void timeout_rr_t3124(void *arg)
+{
+ LOGP(DRR, LOGL_INFO, "timer T3124 has fired\n");
+}
+
static void timeout_rr_t3126(void *arg)
{
struct gsm48_rrlayer *rr = arg;
@@ -569,15 +643,26 @@ static void start_rr_t_monitor(struct gsm48_rrlayer *rr, int sec, int micro)
static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
{
- LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec);
+ LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d.%03d seconds\n", sec,
+ micro / 1000);
rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
rr->t_rel_wait.data = rr;
bsc_schedule_timer(&rr->t_rel_wait, sec, micro);
}
+static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T_starting with %d.%03d seconds\n", sec,
+ micro / 1000);
+ rr->t_starting.cb = timeout_rr_t_starting;
+ rr->t_starting.data = rr;
+ bsc_schedule_timer(&rr->t_starting, sec, micro);
+}
+
static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
{
- LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec);
+ LOGP(DRR, LOGL_INFO, "starting T3110 with %d.%03d seconds\n", sec,
+ micro / 1000);
rr->t3110.cb = timeout_rr_t3110;
rr->t3110.data = rr;
bsc_schedule_timer(&rr->t3110, sec, micro);
@@ -585,15 +670,26 @@ static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
{
- LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec);
+ LOGP(DRR, LOGL_INFO, "starting T3122 with %d.%03d seconds\n", sec,
+ micro / 1000);
rr->t3122.cb = timeout_rr_t3122;
rr->t3122.data = rr;
bsc_schedule_timer(&rr->t3122, sec, micro);
}
+static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3124 with %d.%03d seconds\n", sec,
+ micro / 1000);
+ rr->t3124.cb = timeout_rr_t3124;
+ rr->t3124.data = rr;
+ bsc_schedule_timer(&rr->t3124, sec, micro);
+}
+
static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
{
- LOGP(DRR, LOGL_INFO, "starting T3126 with %d seconds\n", sec);
+ LOGP(DRR, LOGL_INFO, "starting T3126 with %d.%03d seconds\n", sec,
+ micro / 1000);
rr->t3126.cb = timeout_rr_t3126;
rr->t3126.data = rr;
bsc_schedule_timer(&rr->t3126, sec, micro);
@@ -607,6 +703,14 @@ static void stop_rr_t_monitor(struct gsm48_rrlayer *rr)
}
}
+static void stop_rr_t_starting(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t_starting)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n");
+ bsc_del_timer(&rr->t_starting);
+ }
+}
+
static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr)
{
if (bsc_timer_pending(&rr->t_rel_wait)) {
@@ -631,6 +735,14 @@ static void stop_rr_t3122(struct gsm48_rrlayer *rr)
}
}
+static void stop_rr_t3124(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t3124)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n");
+ bsc_del_timer(&rr->t3124);
+ }
+}
+
static void stop_rr_t3126(struct gsm48_rrlayer *rr)
{
if (bsc_timer_pending(&rr->t3126)) {
@@ -731,7 +843,7 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
/* 3.4.7.2 */
if (rr->cipher_on && sc) {
- LOGP(DRR, LOGL_INFO, "cipering already applied.\n");
+ LOGP(DRR, LOGL_NOTICE, "chiphering already applied.\n");
return gsm48_rr_tx_rr_status(ms,
GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
@@ -748,9 +860,7 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
GSM48_RR_CAUSE_CHAN_MODE_UNACCT);
/* change to ciphering */
-#ifdef TODO
- rsl command to activate ciperhing
-#endif
+#warning FIXME: cyphering command
rr->cipher_on = sc, rr->cipher_type = alg_id;
/* response */
@@ -2318,7 +2428,9 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
int ma_len = msgb_l3len(msg) - sizeof(*ia);
uint8_t ch_type, ch_subch, ch_ts;
struct gsm48_rr_cd cd;
+#ifndef TEST_STARTING_TIMER
uint8_t *st, st_len;
+#endif
memset(&cd, 0, sizeof(cd));
@@ -2335,10 +2447,16 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
}
/* starting time */
+#ifdef TEST_STARTING_TIMER
+ cd.start = 1;
+ cd.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+ LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
st_len = ma_len - ia->mob_alloc_len;
st = ia->mob_alloc + ia->mob_alloc_len;
if (st_len >= 3 && st[0] == GSM48_IE_START_TIME)
gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
+#endif
/* decode channel description */
LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
@@ -2386,6 +2504,10 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
ia->mob_alloc_len + 1);
rr->wait_assign = 2;
+ /* reset scheduler */
+ LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
return gsm48_rr_dl_est(ms);
}
LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
@@ -2401,7 +2523,9 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
int ma_len = msgb_l3len(msg) - sizeof(*ia);
uint8_t ch_type, ch_subch, ch_ts;
struct gsm48_rr_cd cd1, cd2;
+#ifndef TEST_STARTING_TIMER
uint8_t *st, st_len;
+#endif
memset(&cd1, 0, sizeof(cd1));
memset(&cd2, 0, sizeof(cd2));
@@ -2418,6 +2542,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
+#ifdef TEST_STARTING_TIMER
+ cd1.start = 1;
+ cd2.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+ memcpy(&cd2, &cd1, sizeof(cd2));
+ LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
/* starting time */
st_len = ma_len - ia->mob_alloc_len;
st = ia->mob_alloc + ia->mob_alloc_len;
@@ -2426,6 +2556,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
(struct gsm48_start_time *)(st+1));
memcpy(&cd2, &cd1, sizeof(cd2));
}
+#endif
/* decode channel description */
LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n");
@@ -2495,6 +2626,10 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
ia->mob_alloc_len + 1);
rr->wait_assign = 2;
+ /* reset scheduler */
+ LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
return gsm48_rr_dl_est(ms);
}
/* request ref 1 */
@@ -2507,6 +2642,10 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
ia->mob_alloc_len + 1);
rr->wait_assign = 2;
+ /* reset scheduler */
+ LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
return gsm48_rr_dl_est(ms);
}
LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
@@ -2564,7 +2703,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
-/* 9.1.1 ADDITIONAL ASSIGMENT is received */
+/* 9.1.1 ADDITIONAL ASSIGMENT is received */
static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -2727,10 +2866,6 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
struct gsm_settings *set = &ms->settings;
uint8_t ch_type, ch_subch, ch_ts;
- /* reset scheduler */
- LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
-
/* setting (new) timing advance */
LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n",
cd->ind_ta, cd->ind_ta - set->alter_delay);
@@ -2759,6 +2894,21 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
return 0;
}
+/* frequency change of channel "after time" */
+static int gsm48_rr_channel_after_time(struct osmocom_ms *ms,
+ struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, uint16_t fn)
+{
+ if (cd->h)
+ l1ctl_tx_dm_freq_req_h1(ms, cd->maio, cd->hsn,
+ ma, ma_len, cd->tsc, fn);
+ else
+ l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn);
+
+#warning FIXME: channel mode, cyphering command
+
+ return 0;
+}
+
/* render list of hopping channels from channel description elements */
static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
uint16_t *ma, uint8_t *ma_len)
@@ -2795,8 +2945,9 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
cd->mob_alloc_lv[0], ma, ma_len, 0);
if (*ma_len < 1) {
LOGP(DRR, LOGL_NOTICE, "mobile allocation with no "
- "frequency\n");
- return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+ "frequency available\n");
+ return GSM48_RR_CAUSE_NO_CELL_ALLOC_A;
+
}
} else
/* decode frequency list */
@@ -2875,13 +3026,6 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
}
}
-#if 0
- if (no CA) {
- LOGP(DRR, LOGL_NOTICE, "No current cell allocation available.\n");
- return GSM48_GSM48_RR_CAUSE_NO_CELL_ALLOC_A;
- }
-#endif
-
return 0;
}
@@ -2897,6 +3041,41 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
uint16_t ma[64];
uint8_t ma_len;
+ /* check if we have to change channel at starting time (we delay) */
+ if (rr->cd_now.start) {
+ int32_t now, start, diff;
+ uint32_t start_mili = 0;
+
+ /* how much time do we have left? */
+ now = ms->meas.last_fn % 42432;
+ start = rr->cd_now.start_tm.fn % 42432;
+ diff = start - now;
+ if (diff < 0)
+ diff += 42432;
+ LOGP(DRR, LOGL_INFO, " (Tnow %d Tstart %d diff %d)\n",
+ now, start, diff);
+ start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+ if (diff >= 32024 || !start_mili) {
+ LOGP(DRR, LOGL_INFO, " -> Start time already "
+ "elapsed\n");
+ rr->cd_now.start = 0;
+ } else {
+ LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
+ "future\n", start_mili);
+ }
+
+#ifndef TEST_FREQUENCY_MOD
+ /* schedule start of IMM.ASS */
+ rr->modify_state = GSM48_RR_MOD_IMM_ASS;
+ start_rr_t_starting(rr, start_mili / 1000,
+ (start_mili % 1000) * 1000);
+ /* when timer fires, start time is already elapsed */
+ rr->cd_now.start = 0;
+
+ return 0;
+#endif
+ }
+
/* get hopping sequence, if required */
if (gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len))
return -EINVAL;
@@ -2950,8 +3129,22 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
memcpy(pr->data, mi + 1, 1 + mi[1]);
}
+#ifdef TEST_FREQUENCY_MOD
+ LOGP(DRR, LOGL_INFO, " TESTING: frequency modify IMM.ASS\n");
+ memcpy(&rr->cd_before, &rr->cd_now, sizeof(rr->cd_before));
+ rr->cd_before.h = 0;
+ rr->cd_before.arfcn = 0;
+ /* activate channel */
+ gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len);
+ /* render channel "after time" */
+ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+ /* schedule change of channel */
+ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+ rr->cd_now.start_tm.fn);
+#else
/* activate channel */
gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+#endif
/* start establishmnet */
return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
@@ -3064,9 +3257,88 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
}
/*
- * chanel mode modify, assignment, and handover
+ * frequency redefition, chanel mode modify, assignment, and handover
*/
+/* 9.1.13 FREQUENCY REDEFINITION is received */
+static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_frq_redef *fr = msgb_l3(msg);
+ int mob_al_len = msgb_l3len(msg) - sizeof(*fr);
+ uint8_t ch_type, ch_subch, ch_ts;
+ struct gsm48_rr_cd cd;
+ uint8_t cause;
+ uint8_t *st;
+ uint16_t ma[64];
+ uint8_t ma_len;
+
+ memcpy(&cd, &rr->cd_now, sizeof(cd));
+
+ if (mob_al_len < 0 /* mobile allocation IE must be included */
+ || fr->mob_alloc_len + 2 > mob_al_len) { /* short read of IE */
+ LOGP(DRR, LOGL_NOTICE, "Short read of FREQUENCY REDEFINITION "
+ "message.\n");
+ return -EINVAL;
+ }
+ if (fr->mob_alloc_len > 8) {
+ LOGP(DRR, LOGL_NOTICE, "Moble allocation in FREQUENCY "
+ "REDEFINITION too large.\n");
+ return -EINVAL;
+ }
+
+ /* decode channel description */
+ LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n");
+ cd.chan_nr = fr->chan_desc.chan_nr;
+ rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (fr->chan_desc.h0.h) {
+ cd.h = 1;
+ gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio,
+ &cd.hsn);
+ LOGP(DRR, LOGL_INFO, " (MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+ cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc);
+ } else {
+ cd.h = 0;
+ gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn);
+ LOGP(DRR, LOGL_INFO, " (ARFCN %u TS %u SS %u TSC %u)\n",
+ cd.arfcn, ch_ts, ch_subch, cd.tsc);
+ }
+
+ /* mobile allocation */
+ memcpy(&rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len,
+ fr->mob_alloc_len + 1);
+
+ /* starting time */
+ st = fr->mob_alloc + fr->mob_alloc_len;
+ gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
+
+ /* cell channel description */
+ if (mob_al_len >= fr->mob_alloc_len + 2 + 17
+ && fr->mob_alloc[fr->mob_alloc_len + 2] == GSM48_IE_CELL_CH_DESC) {
+ const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 3 + 1;
+
+ LOGP(DRR, LOGL_INFO, " using cell channel description)\n");
+ memcpy(&cd.cell_desc_lv + 1, v, 17);
+ cd.cell_desc_lv[0] = 16;
+ }
+
+ /* render channel "after time" */
+ cause = gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+ if (cause)
+ return gsm48_rr_tx_rr_status(ms, cause);
+
+ /* update to new channel data */
+ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
+
+ /* schedule change of channel */
+ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+ rr->cd_now.start_tm.fn);
+
+ rr->cd_now.start = 0;
+
+ return 0;
+}
+
/* 9.1.6 sending CHANNEL MODE MODIFY ACKNOWLEDGE */
static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms,
struct gsm48_chan_desc *cd, uint8_t mode)
@@ -3146,7 +3418,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DRR, LOGL_ERROR, "Mode %u not supported!\n", mode);
}
rr->cd_now.mode = mode;
-#warning FIXME: set mode
+#warning FIXME: channel mode
return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, mode);
}
@@ -3273,20 +3545,18 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
}
/* starting time */
+#ifdef TEST_STARTING_TIMER
+ cda->start = 1;
+ cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+ LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {
gsm48_decode_start_time(cda, (struct gsm48_start_time *)
TLVP_VAL(&tp, GSM48_IE_START_TIME));
/* 9.1.2.5 "... before time IE is not present..." */
if (!before_time) {
LOGP(DRR, LOGL_INFO, " -> channel description after "
- "time only, using it before time also\n");
- before_time = 1;
- cdb->tsc = cda->tsc;
- cdb->h = cda->h;
- cdb->arfcn = cda->arfcn;
- cdb->maio = cda->maio;
- cdb->hsn = cda->hsn;
- cdb->chan_nr = cda->chan_nr;
+ "time only, but starting time\n");
} else
LOGP(DRR, LOGL_INFO, " -> channel description before "
"and after time\n");
@@ -3298,6 +3568,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
"time, but no starting time, ignoring!\n");
}
}
+#endif
/* mobile allocation / frequency list after time */
if (cda->h) {
@@ -3432,30 +3703,27 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d)\n", cda->ind_tx_power,
cda->ind_ta);
- /* check if we have to change channel "before time" */
- if (before_time) {
- uint16_t now, start, diff;
+ /* check if we have to change channel at starting time */
+ if (cda->start) {
+ int32_t now, start, diff;
/* how much time do we have left? */
now = ms->meas.last_fn % 42432;
start = cda->start_tm.fn % 42432;
- diff = (start - now) % 42432;
- LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d)\n", now,
- start);
- if (diff >= 32024) {
- LOGP(DRR, LOGL_INFO, " -> Start time is in the past\n");
+ diff = start - now;
+ if (diff < 0)
+ diff += 42432;
+ LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
+ now, start, diff);
+ start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+ if (diff >= 32024 || !start_mili) {
+ LOGP(DRR, LOGL_INFO, " -> Start time already "
+ "elapsed\n");
before_time = 0;
+ cda->start = 0;
} else {
- start_mili = (uint32_t)diff * 19580 / 42432 * 10;
LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
"future\n", start_mili);
- /* GSM 05.10 Clause 6.8 */
- if (start_mili < 120) {
- LOGP(DRR, LOGL_INFO, " -> Start time too close "
- "in the future, ignoring channel "
- "before time\n");
- before_time = 0;
- }
}
}
@@ -3477,22 +3745,400 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
}
#endif
+#ifdef TEST_FREQUENCY_MOD
+ LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n");
+ before_time = 1;
+ memcpy(cdb, cda, sizeof(*cdb));
+ cdb->h = 0;
+ cdb->arfcn = 0;
+#endif
+
/* schedule start of assignment */
- if (before_time) {
- LOGP(DRR, LOGL_INFO, "FIXME starting time not supported yet\n");
- return -ENOTSUP;
+ rr->modify_state = GSM48_RR_MOD_ASSIGN;
+ if (!before_time && cda->start) {
+ start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
+ /* when timer fires, start time is already elapsed */
+ cda->start = 0;
+
+ return 0;
}
- /* start suspension of current link */
+ /* if no starting time, start suspension of current link directly */
LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
return -ENOMEM;
gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg);
- /* change into special assignment suspension state */
- rr->assign_susp_state = 1;
- rr->resume_last_state = 0;
+ return 0;
+}
+
+/* 9.1.16 sending HANDOVER COMPLETE */
+static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_ho_cpl *hc;
+
+ LOGP(DRR, LOGL_INFO, "HANDOVER COMPLETE (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ hc = (struct gsm48_ho_cpl *) msgb_put(nmsg, sizeof(*hc));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_HANDO_COMPL;
+
+ /* RR_CAUSE */
+ hc->rr_cause = cause;
+
+ // FIXME: mobile observed time
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* 9.1.4 sending HANDOVER FAILURE */
+static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_ho_fail *hf;
+
+ LOGP(DRR, LOGL_INFO, "HANDOVER FAILURE (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ hf = (struct gsm48_ho_fail *) msgb_put(nmsg, sizeof(*hf));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+ /* RR_CAUSE */
+ hf->rr_cause = cause;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* receiving HANDOVER COMMAND message (9.1.15) */
+static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho);
+ struct tlv_parsed tp;
+ struct gsm48_rr_cd *cda = &rr->cd_after;
+ struct gsm48_rr_cd *cdb = &rr->cd_before;
+ uint16_t arfcn;
+ uint8_t bcc, ncc;
+ uint8_t ch_type, ch_subch, ch_ts;
+ uint8_t before_time = 0;
+ uint16_t ma[64];
+ uint8_t ma_len;
+ uint32_t start_mili = 0;
+ uint8_t cause;
+ struct msgb *nmsg;
+
+ LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n");
+
+ memset(cda, 0, sizeof(*cda));
+ memset(cdb, 0, sizeof(*cdb));
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND "
+ "message.\n");
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+
+ /* cell description */
+ gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc);
+
+ /* handover reference */
+ rr->chan_req_val = ho->ho_ref;
+ rr->chan_req_mask = 0x00;
+
+ /* sync ind */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) {
+ gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *)
+ TLVP_VAL(&tp, GSM48_IE_SYNC_IND));
+ LOGP(DRR, LOGL_INFO, " (sync_ind=%d rot=%d nci=%d)\n",
+ rr->hando_sync_ind, rr->hando_rot, rr->hando_nci);
+ }
+
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
+
+ /* decode channel description (before time) */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
+ struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
+ TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
+ cdb->chan_nr = ccd->chan_nr;
+ rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (ccd->h0.h) {
+ cdb->h = 1;
+ gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
+ &cdb->hsn);
+ LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u "
+ "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+ cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc);
+ } else {
+ cdb->h = 0;
+ gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
+ LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
+ "ARFCN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+ cdb->arfcn, ch_ts, ch_subch, cdb->tsc);
+ }
+ before_time = 1;
+ }
+
+ /* decode channel description (after time) */
+ cda->chan_nr = ho->chan_desc.chan_nr;
+ rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (ho->chan_desc.h0.h) {
+ cda->h = 1;
+ gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio,
+ &cda->hsn);
+ LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u "
+ "TS %u SS %u TSC %u)\n", ho->chan_desc.chan_nr,
+ cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc);
+ } else {
+ cda->h = 0;
+ gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn);
+ LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %u TS %u "
+ "SS %u TSC %u)\n", ho->chan_desc.chan_nr,
+ cda->arfcn, ch_ts, ch_subch, cda->tsc);
+ }
+
+ /* starting time */
+#ifdef TEST_STARTING_TIMER
+ cda->start = 1;
+ cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+ LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
+ if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {
+ gsm48_decode_start_time(cda, (struct gsm48_start_time *)
+ TLVP_VAL(&tp, GSM48_IE_START_TIME));
+ /* 9.1.2.5 "... before time IE is not present..." */
+ if (!before_time) {
+ LOGP(DRR, LOGL_INFO, " -> channel description after "
+ "time only, but starting time\n");
+ } else
+ LOGP(DRR, LOGL_INFO, " -> channel description before "
+ "and after time\n");
+ } else {
+ /* 9.1.2.5 "... IEs unnecessary in this message." */
+ if (before_time) {
+ before_time = 0;
+ LOGP(DRR, LOGL_INFO, " -> channel description before "
+ "time, but no starting time, ignoring!\n");
+ }
+ }
+#endif
+
+ /* mobile allocation / frequency list after time */
+ if (cda->h) {
+ if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
+ const uint8_t *lv =
+ TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1;
+
+ LOGP(DRR, LOGL_INFO, " after: hopping required and "
+ "mobile allocation available\n");
+ if (*lv + 1 > sizeof(cda->mob_alloc_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cda->mob_alloc_lv, lv, *lv + 1);
+ } else
+ if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
+ const uint8_t *lv =
+ TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1;
+
+ LOGP(DRR, LOGL_INFO, " after: hopping required and "
+ "frequency list available\n");
+ if (*lv + 1 > sizeof(cda->freq_list_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cda->freq_list_lv, lv, *lv + 1);
+ } else {
+ LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
+ "no mobile allocation / frequency list\n");
+ }
+ }
+
+ /* mobile allocation / frequency list before time */
+ if (cdb->h) {
+ if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) {
+ const uint8_t *lv =
+ TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1;
+
+ LOGP(DRR, LOGL_INFO, " before: hopping required and "
+ "mobile allocation available\n");
+ if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cdb->mob_alloc_lv, lv, *lv + 1);
+ } else
+ if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
+ const uint8_t *lv =
+ TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1;
+
+ LOGP(DRR, LOGL_INFO, " before: hopping required and "
+ "frequency list available\n");
+ if (*lv + 1 > sizeof(cdb->freq_list_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cdb->freq_list_lv, lv, *lv + 1);
+ } else
+ if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
+ const uint8_t *lv =
+ TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE) - 1;
+
+ LOGP(DRR, LOGL_INFO, " before: hopping required and "
+ "frequency channel sequence available\n");
+ if (*lv + 1 > sizeof(cdb->freq_seq_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cdb->freq_seq_lv, lv, *lv + 1);
+ } else
+ if (cda->mob_alloc_lv[0]) {
+ LOGP(DRR, LOGL_INFO, " before: hopping required and "
+ "mobile allocation not available, using "
+ "mobile allocation after time\n");
+ memcpy(&cdb->mob_alloc_lv, &cda->mob_alloc_lv,
+ sizeof(cdb->mob_alloc_lv));
+ if (cda->freq_list_lv[0]) {
+ LOGP(DRR, LOGL_INFO, " before: hopping required and "
+ "frequency list not available, using "
+ "frequency list after time\n");
+ memcpy(&cdb->freq_list_lv, &cda->freq_list_lv,
+ sizeof(cdb->freq_list_lv));
+ } else {
+ LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
+ "no mobile allocation / frequency list\n");
+ }
+ }
+
+ /* cell channel description */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
+ const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC) - 1;
+
+ LOGP(DRR, LOGL_INFO, " both: using cell channel description "
+ "in case of mobile allocation\n");
+ if (*lv + 1 > sizeof(cdb->cell_desc_lv)) {
+ LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+ return -ENOMEM;
+ }
+ memcpy(&cdb->cell_desc_lv, lv, *lv + 1);
+ memcpy(&cda->cell_desc_lv, lv, *lv + 1);
+ } else {
+ /* keep old */
+ memcpy(&cdb->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+ sizeof(cdb->cell_desc_lv));
+ memcpy(&cda->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+ sizeof(cda->cell_desc_lv));
+ }
+
+ /* channel mode */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
+ cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
+ LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n",
+ cda->mode);
+ } else
+ cda->mode = cdb->mode = rr->cd_now.mode;
+
+ /* cipher mode setting */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
+ cda->cipher = cdb->cipher =
+ *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
+ LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n",
+ cda->cipher);
+ } else
+ cda->cipher = cdb->cipher = rr->cd_now.cipher;
+
+ /* power command and TA (before and after time) */
+ gsm48_decode_power_cmd_acc(
+ (struct gsm48_power_cmd *) &ho->power_command,
+ &cda->ind_tx_power, &rr->hando_act);
+ cdb->ind_tx_power = cda->ind_tx_power;
+ cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */
+ LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d access=%s)\n",
+ cda->ind_tx_power, cda->ind_ta,
+ (rr->hando_act) ? "optional" : "mandatory");
+
+ /* check if we have to change channel at starting time */
+ if (cda->start) {
+ int32_t now, start, diff;
+
+ /* how much time do we have left? */
+ now = ms->meas.last_fn % 42432;
+ start = cda->start_tm.fn % 42432;
+ diff = start - now;
+ if (diff < 0)
+ diff += 42432;
+ LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
+ now, start, diff);
+ start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+ if (diff >= 32024 || !start_mili) {
+ LOGP(DRR, LOGL_INFO, " -> Start time already "
+ "elapsed\n");
+ before_time = 0;
+ cda->start = 0;
+ } else {
+ LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
+ "future\n", start_mili);
+ }
+ }
+
+ /* check if channels are valid */
+ if (before_time) {
+ cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
+ if (cause)
+ return gsm48_rr_tx_hando_fail(ms, cause);
+ }
+ cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
+ if (cause)
+ return gsm48_rr_tx_hando_fail(ms, cause);
+
+
+#if 0
+ if (not supported) {
+ LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n");
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT;
+ }
+#endif
+
+#ifdef TEST_FREQUENCY_MOD
+ LOGP(DRR, LOGL_INFO, " TESTING: frequency modify HANDO.CMD\n");
+ before_time = 1;
+ memcpy(cdb, cda, sizeof(*cdb));
+ cdb->h = 0;
+ cdb->arfcn = 0;
+#endif
+
+ /* schedule start of handover */
+ rr->modify_state = GSM48_RR_MOD_HANDO;
+ if (!before_time && cda->start) {
+ start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
+ /* when timer fires, start time is already elapsed */
+ cda->start = 0;
+
+ return 0;
+ }
+
+ /* if no starting time, start suspension of current link directly */
+ LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg);
return 0;
}
@@ -3516,20 +4162,28 @@ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
- if (rr->hando_susp_state || rr->assign_susp_state) {
- LOGP(DRR, LOGL_INFO, "data link is resumed\n");
+ LOGP(DRR, LOGL_INFO, "data link is resumed\n");
- if (rr->resume_last_state) {
- rr->resume_last_state = 0;
- gsm48_rr_tx_ass_fail(ms,
- GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
- } else {
- gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
- }
- /* transmit queued frames during ho / ass transition */
- gsm48_rr_dequeue_down(ms);
+ switch (rr->modify_state) {
+ case GSM48_RR_MOD_ASSIGN:
+ gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+ break;
+ case GSM48_RR_MOD_HANDO:
+ gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+ break;
+ case GSM48_RR_MOD_ASSIGN_RESUME:
+ gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ break;
+ case GSM48_RR_MOD_HANDO_RESUME:
+ gsm48_rr_tx_hando_fail(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ break;
}
+ /* transmit queued frames during ho / ass transition */
+ gsm48_rr_dequeue_down(ms);
+
+ rr->modify_state = GSM48_RR_MOD_NONE;
+
return 0;
}
@@ -3538,7 +4192,7 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
- if (rr->hando_susp_state || rr->assign_susp_state) {
+ if (rr->modify_state) {
struct msgb *nmsg;
uint16_t ma[64];
uint8_t ma_len;
@@ -3552,12 +4206,31 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
/* store current channel descriptions */
memcpy(&rr->cd_last, &rr->cd_now, sizeof(rr->cd_last));
+
/* copy channel description "after time" */
memcpy(&rr->cd_now, &rr->cd_after, sizeof(rr->cd_now));
- /* render and activate channel */
- gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
- gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+ if (rr->cd_after.start) {
+ /* render channel "before time" */
+ gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len);
+
+ /* activate channel */
+ gsm48_rr_activate_channel(ms, &rr->cd_before, ma,
+ ma_len);
+
+ /* render channel "after time" */
+ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+
+ /* schedule change of channel */
+ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+ rr->cd_now.start_tm.fn);
+ } else {
+ /* render channel "after time" */
+ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+
+ /* activate channel */
+ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+ }
/* send DL-RESUME REQUEST */
LOGP(DRR, LOGL_INFO, "request resume of data link\n");
@@ -3568,7 +4241,7 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
#ifdef TODO
/* trigger RACH */
- if (rr->hando_susp_state) {
+ if (rr->modify_state == GSM48_RR_MOD_HANDO) {
gsm48_rr_tx_hando_access(ms);
rr->hando_acc_left = 3;
}
@@ -3701,7 +4374,8 @@ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
gsm48_apply_v_sd(rr, msg);
/* queue message, during handover or assignment procedure */
- if (rr->hando_susp_state || rr->assign_susp_state) {
+ if (rr->modify_state == GSM48_RR_MOD_ASSIGN
+ || rr->modify_state == GSM48_RR_MOD_HANDO) {
LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n");
msgb_enqueue(&rr->downqueue, msg);
return 0;
@@ -3746,17 +4420,18 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
case GSM48_MT_RR_CHAN_MODE_MODIF:
rc = gsm48_rr_rx_chan_modify(ms, msg);
break;
-#if 0
case GSM48_MT_RR_HANDO_CMD:
rc = gsm48_rr_rx_hando_cmd(ms, msg);
break;
case GSM48_MT_RR_FREQ_REDEF:
- rc = gsm48_rr_rx_freq_redef(ms, msg);
+ rc = gsm48_rr_rx_frq_redef(ms, msg);
break;
-#endif
case GSM48_MT_RR_CHAN_REL:
rc = gsm48_rr_rx_chan_rel(ms, msg);
break;
+ case GSM48_MT_RR_APP_INFO:
+ LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n");
+ break;
default:
LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
gh->msg_type);
@@ -4020,6 +4695,80 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+/* MDL-ERROR */
+static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+ uint8_t *mode;
+ uint8_t cause = rllh->data[2];
+ uint16_t ma[64];
+ uint8_t ma_len;
+
+ switch (cause) {
+ case RLL_CAUSE_SEQ_ERR:
+ case RLL_CAUSE_UNSOL_DM_RESP_MF:
+ break;
+ default:
+ LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) ignoring\n",
+ cause);
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) aborting\n", cause);
+
+ /* disconnect the main signalling link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 1; /* local release */
+ gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg);
+
+ /* deactivate channel */
+ l1ctl_tx_dm_rel_req(rr->ms);
+
+ switch (rr->modify_state) {
+ case GSM48_RR_MOD_ASSIGN:
+ case GSM48_RR_MOD_HANDO:
+ if (rr->modify_state == GSM48_RR_MOD_ASSIGN)
+ rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
+ else
+ rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
+
+ /* get old channel description */
+ memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now));
+
+ /* render and change radio to old channel */
+ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+
+ /* re-establish old link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
+ case GSM48_RR_MOD_ASSIGN_RESUME:
+ case GSM48_RR_MOD_HANDO_RESUME:
+ rr->modify_state = GSM48_RR_MOD_NONE;
+ break;
+ }
+
+ /* send abort ind to upper layer */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_LINK_FAILURE;
+ gsm48_rr_upmsg(ms, nmsg);
+
+ /* return idle */
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+ return 0;
+}
+
/*
* state machines
*/
@@ -4065,10 +4814,10 @@ static struct dldatastate {
#if 0
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
-
- {SBIT(GSM_RRSTATE),
- RSL_MT_MDL_ERROR_IND, gsm48_rr_mdl_error_ind},
#endif
+
+ {SBIT(GSM48_RR_ST_DEDICATED),
+ RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
};
#define DLDATASLLEN \
@@ -4173,11 +4922,6 @@ static struct rrdownstate {
{SBIT(GSM48_RR_ST_CONN_PEND) |
SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},
-
-#if 0
- {SBIT(GSM48_RR_ST_DEDICATED),
- GSM48_RR_ACT_REQ, gsm48_rr_act_req},
-#endif
};
#define RRDOWNSLLEN \
@@ -4256,9 +5000,11 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
}
stop_rr_t_monitor(rr);
+ stop_rr_t_starting(rr);
stop_rr_t_rel_wait(rr);
stop_rr_t3110(rr);
stop_rr_t3122(rr);
+ stop_rr_t3124(rr);
stop_rr_t3126(rr);
return 0;
@@ -4269,109 +5015,6 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
todo rr_sync_ind when receiving ciph, re ass, channel mode modify
-/* decode "Cell Description" (10.5.2.2) */
-static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
-{
- *arfcn = (cd->bcch_hi << 8) + cd->bcch_lo;
- *ncc = cd->ncc;
- *bcc = cd->bcc;
-}
-
-/* decode "Synchronization Indication" (10.5.2.39) */
-static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr, struct gsm48_rr_sync_ind *si)
-{
- rr->ho_sync_ind = si->si;
- rr->ho_rot = si->rot;
- rr->ho_nci = si->nci;
-}
-
-/* receiving HANDOVER COMMAND message (9.1.15) */
-static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm48_rrlayer *rr = ms->rrlayer;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
- int payload_len = msgb_l3len(msg) - sizeof(*gh) - wirklich sizeof(*ho);
- struct tlv_parsed tp;
- struct gsm48_rr_cd cd;
- struct msgb *nmsg;
-
- memset(&cd, 0, sizeof(cd));
-
- if (payload_len < 0) {
- LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message.\n");
- return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
- }
- tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
-
-TODO
-
- /* start suspension of current link */
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg);
-
- /* change into special handover suspension state */
- rr->hando_susp_state = 1;
- rr->resume_last_state = 0;
-
- return 0;
-}
-
-static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm48_rrlayer *rr = ms->rrlayer;
- struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- struct msgb *nmsg;
- uint8_t cause = rllh->data[2];
-
- printing of the cause
-
- switch (cause) {
- case RLL_CAUSE_SEQ_ERR:
- case RLL_CAUSE_UNSOL_DM_RESP_MF:
- einige muessen ignoriert werden
- andere gelten als release
- }
-
- if (rr->hando_susp_state || rr->assign_susp_state) {
- if (!rr->resume_last_state) {
- uint16_t ma[64];
- uint8_t ma_len;
-
- rr->resume_last_state = 1;
-
- /* get old channel description */
- memcpy(&rr->cd_now, &rr->chan_last, sizeof(rr->cd_now));
-
- /* render and change radio to old channel */
- gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
- gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
-
- /* re-establish old link */
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
- }
- rr->resume_last_state = 0;
- }
-
- /* deactivate channel */
- l1ctl_tx_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
-
- /* send abort ind to upper layer */
- nmsg = gsm48_mm_msgb_alloc();
-
- if (!msg)
- return -ENOMEM;
- nrrh = (struct gsm_mm_hdr *)nmsg->data;
- nrrh->msg_type = RR_ABORT_IND;
- nrrh->cause = GSM_MM_CAUSE_LINK_FAILURE;
- return gsm48_rr_upmsg(ms, msg);
-}
-
static void timeout_rr_t3124(void *arg)
{
struct gsm48_rrlayer *rr = arg;
@@ -4381,7 +5024,7 @@ static void timeout_rr_t3124(void *arg)
hando_acc_left = 0;
/* get old channel description */
- memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
+ memcpy(&rr->chan_desc, &rr->chan_last, sizeof(rr->chan_desc));
/* change radio to old channel */
tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
@@ -4414,7 +5057,7 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
struct msgb *nmsg;
int s;
- if (!rr->hando_susp_state) {
+ if (rr->modify_state != GSM48_RR_MOD_HANDO) {
LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n");
return 0;
}