summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-12-11 11:35:14 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2016-09-25 08:11:43 +0200
commit6188bdc5481a936a66b7c03e29e53c78a3ba62e7 (patch)
treee475bec473cc89df8b1e659e85c407b7d89f36ac
parent07e03e1fcadb559accc8725b8013d54f0b7fd6d6 (diff)
[WIP] HO: Completed handover process at mobile (layer 3).
The implementation supports: - Non-synchronised handover - Synchronized handover (tbd) - Pre-sychronized handover
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h24
-rw-r--r--src/host/layer23/src/mobile/gsm322.c5
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c543
3 files changed, 396 insertions, 176 deletions
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
index 6235bfd..d0227c6 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
@@ -9,6 +9,9 @@
#define T200_DCCH_SHARED 2 /* SDCCH shares SAPI 0 and 3 */
#define T200_ACCH 2 /* SACCH SAPI 3 */
+/* add some extra time due to async nature of L1CTL */
+#define GSM_T3124_675 0, 975000
+#define GSM_T3124_320 0, 620000
/* GSM 04.07 9.1.2 */
#define GSM48_RR_EST_REQ 0x10
@@ -83,6 +86,12 @@ struct gsm48_rr_hdr {
#define GSM48_RR_MOD_ASSIGN_RESUME 4
#define GSM48_RR_MOD_HANDO_RESUME 5
+/* handover sync indications (TS 04.08 10.5.2.39) */
+#define GSM48_RR_HO_NON_SYNC 0
+#define GSM48_RR_HO_SYNC 1
+#define GSM48_RR_HO_PRE_SYNC 2
+#define GSM48_RR_HO_PSEUDO_SYNC 3
+
/* channel description */
struct gsm48_rr_cd {
uint8_t tsc;
@@ -113,7 +122,7 @@ struct gsm48_rr_meas {
/* note: must be sorted by arfcn 1..1023,0 according to SI5* */
uint8_t nc_num; /* number of measured cells (32 max) */
int8_t nc_rxlev_dbm[32]; /* -128 = no value */
- uint8_t nc_bsic[32];
+ uint8_t nc_bsic[32]; /* 255 = no value */
uint16_t nc_arfcn[32];
};
@@ -171,11 +180,20 @@ struct gsm48_rrlayer {
/* special states when assigning channel */
uint8_t modify_state;
- uint8_t hando_sync_ind, hando_rot, hando_nci, hando_act;
struct gsm48_rr_cd cd_last; /* store last cd in case of failure */
struct gsm48_rr_cd cd_before; /* before start time */
struct gsm48_rr_cd cd_after; /* after start time */
+ /* handover */
+ int hando_meas_index;
+ /* handover refers to this neighbor */
+ uint8_t hando_sync_ind, hando_rot, hando_nci, hando_act;
+ uint8_t hando_ref; /* handover reference */
+ uint8_t hando_acc_pending;
+ /* counter for access bursts */
+ uint16_t hando_new_cell_arfcn;
+ uint8_t hando_new_cell_bsic;
+
/* BA range */
uint8_t ba_ranges;
uint32_t ba_range[16];
@@ -211,5 +229,7 @@ int gsm48_rr_stop_monitor(struct osmocom_ms *ms);
int gsm48_rr_alter_delay(struct osmocom_ms *ms);
int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg);
int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode);
+int gsm48_rr_meas_ind(struct osmocom_ms *ms, uint16_t band_arfcn,
+ uint8_t rx_lev, uint8_t bsic);
#endif /* _GSM48_RR_H */
diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c
index 9166089..92446e3 100644
--- a/src/host/layer23/src/mobile/gsm322.c
+++ b/src/host/layer23/src/mobile/gsm322.c
@@ -3052,12 +3052,11 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
case S_L1CTL_NEIGH_PM_IND:
ni = signal_data;
ms = ni->ms;
-#ifdef COMMING_LATE_R
/* in dedicated mode */
if (ms->rrlayer.dm_est)
- gsm48_rr_meas_ind(ms, ni->band_arfcn, ni->rx_lev);
+ gsm48_rr_meas_ind(ms, ni->band_arfcn, ni->rx_lev,
+ ni->bsic);
else
-#endif
/* in camping mode */
if ((ms->cellsel.state == GSM322_C3_CAMPED_NORMALLY
|| ms->cellsel.state == GSM322_C7_CAMPED_ANY_CELL)
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index ec053fd..c600d86 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -91,6 +91,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms);
static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
uint8_t mode);
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_rr_as_ho_failed(struct osmocom_ms *ms);
/*
* support
@@ -641,8 +642,9 @@ static void timeout_rr_meas(void *arg)
rxlev = (meas->rxlev + meas->frames / 2) / meas->frames;
berr = (meas->berr + meas->frames / 2) / meas->frames;
snr = (meas->snr + meas->frames / 2) / meas->frames;
- sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d "
- "LAI=%s %s %04x ID=%04x", cs->sel_arfcn,
+ sprintf(text, "MON: BCCH=%s lev=%s snr=%2d ber=%3d "
+ "LAI=%s %s %04x ID=%04x",
+ gsm_print_arfcn(cs->sel_arfcn),
gsm_print_rxlev(rxlev), berr, snr,
gsm_print_mcc(cs->sel_mcc),
gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id);
@@ -743,7 +745,12 @@ static void timeout_rr_t3122(void *arg)
static void timeout_rr_t3124(void *arg)
{
- LOGP(DRR, LOGL_INFO, "timer T3124 has fired\n");
+ struct gsm48_rrlayer *rr = arg;
+ struct osmocom_ms *ms = rr->ms;
+
+ LOGP(DRR, LOGL_INFO, "timer T3124 has fired (handover timeout)\n");
+
+ gsm48_rr_as_ho_failed(ms);
}
static void timeout_rr_t3126(void *arg)
@@ -1698,6 +1705,94 @@ fail:
}
/*
+ * neighbour cell measurement and sync (dedicated mode)
+ */
+
+/* generate list from SI5 and send to L1 to start SB measurement */
+static int gsm48_new_si5(struct osmocom_ms *ms)
+{
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_rr_meas *rrmeas = &rr->meas;
+ int n = 0, i, refer_pcs;
+
+ rrmeas->nc_num = 0;
+ refer_pcs = gsm_refer_pcs(cs->arfcn, s);
+
+ /* collect channels from freq list (1..1023,0) */
+ for (i = 1; i <= 1024; i++) {
+ if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) {
+ if (n == 32) {
+ LOGP(DRR, LOGL_NOTICE, "SI5* report "
+ "exceeds 32 BCCHs\n");
+ break;
+ }
+ if (refer_pcs && i >= 512 && i <= 810) {
+ if (!(cs->list[i - 512 + 1024].flags
+ & GSM322_CS_FLAG_SUPPORT)) {
+ LOGP(DRR, LOGL_NOTICE, "SI5* report "
+ "arfcn %s not supported\n",
+ gsm_print_arfcn(i | ARFCN_PCS));
+ continue;
+ }
+ rrmeas->nc_arfcn[n] = i | ARFCN_PCS;
+ } else {
+ if (!(cs->list[i & 1023].flags
+ & GSM322_CS_FLAG_SUPPORT)) {
+ LOGP(DRR, LOGL_NOTICE, "SI5* report "
+ "arfcn %s not supported\n",
+ gsm_print_arfcn(i & 1023));
+ continue;
+ }
+ rrmeas->nc_arfcn[n] = i & 1023;
+ }
+ /* set rxlev + bsic status to "no value" */
+ rrmeas->nc_rxlev_dbm[n] = -128;
+ rrmeas->nc_bsic[n] = 255;
+ LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n",
+ gsm_print_arfcn(rrmeas->nc_arfcn[n]));
+ n++;
+ }
+ }
+ rrmeas->nc_num = n;
+
+ /* in case we receive this message after leaving dedicated mode. */
+ if (!rr->dm_est)
+ return -EIO;
+
+ /* start neigbour cell measurement and sync task */
+ LOGP(DRR, LOGL_INFO, "Sending list of neighbour cells to layer1.\n");
+ l1ctl_tx_neigh_pm_req(ms, n, rrmeas->nc_arfcn);
+
+ return n;
+}
+
+int gsm48_rr_meas_ind(struct osmocom_ms *ms, uint16_t band_arfcn,
+ uint8_t rx_lev, uint8_t bsic)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_rr_meas *rrmeas = &rr->meas;
+ int i;
+
+ LOGP(DRR, LOGL_INFO, "Measurement result: arfcn=%s lev=%s bsic=",
+ gsm_print_arfcn(band_arfcn), gsm_print_rxlev(rx_lev));
+ if (bsic > 0x3f)
+ LOGPC(DRR, LOGL_INFO, "<unknown>\n");
+ else
+ LOGPC(DRR, LOGL_INFO, "%d,%d\n", bsic >> 3, bsic & 7);
+
+ for (i = 0; i < rrmeas->nc_num; i++) {
+ if (rrmeas->nc_arfcn[i] == band_arfcn) {
+ rrmeas->nc_rxlev_dbm[i] = rx_lev - 110;
+ rrmeas->nc_bsic[i] = bsic;
+ }
+ }
+
+ return 0;
+}
+
+/*
* system information
*/
@@ -1705,7 +1800,6 @@ fail:
static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
{
struct gsm48_sysinfo *s = ms->cellsel.si;
- struct gsm322_cellsel *cs = &ms->cellsel;
struct msgb *nmsg;
struct gsm322_msg *em;
@@ -1716,33 +1810,9 @@ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
|| type == GSM48_MT_RR_SYSINFO_5ter)
&& s->si5
&& (!s->nb_ext_ind_si5 || s->si5bis)) {
- struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
- int n = 0, i, refer_pcs;
-
LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
s->nb_ba_ind_si5);
- rrmeas->nc_num = 0;
- refer_pcs = gsm_refer_pcs(cs->arfcn, s);
-
- /* collect channels from freq list (1..1023,0) */
- for (i = 1; i <= 1024; i++) {
- if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) {
- if (n == 32) {
- LOGP(DRR, LOGL_NOTICE, "SI5* report "
- "exceeds 32 BCCHs\n");
- break;
- }
- if (refer_pcs && i >= 512 && i <= 810)
- rrmeas->nc_arfcn[n] = i | ARFCN_PCS;
- else
- rrmeas->nc_arfcn[n] = i & 1023;
- rrmeas->nc_rxlev_dbm[n] = -128;
- LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n",
- gsm_print_arfcn(rrmeas->nc_arfcn[n]));
- n++;
- }
- }
- rrmeas->nc_num = n;
+ gsm48_new_si5(ms);
}
/* send sysinfo event to other layers */
@@ -2734,7 +2804,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
serv_rxqual_sub = 0;
uint8_t ta, tx_power;
uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0, multi_rep = 0;
- uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6];
+ uint8_t n = 0, i, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6];
/* just in case! */
if (!s)
@@ -2764,9 +2834,10 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
memset(&bsic_nc, 0, sizeof(bsic_nc));
memset(&bcch_f_nc, 0, sizeof(bcch_f_nc));
if (rep_valid) {
- int8_t strongest, current;
+ int8_t current;
uint8_t ncc;
- int i, index;
+ int index;
+ int checked[32];
/* multiband reporting, if not: 0 = normal reporting */
if (s->si5ter)
@@ -2774,22 +2845,28 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
/* get 6 strongest measurements */
// FIXME: multiband report
- strongest = 127; /* infinite */
+ memset(checked, 0, sizeof(checked));
for (n = 0; n < 6; n++) {
current = -128; /* -infinite */
index = 0;
for (i = 0; i < rrmeas->nc_num; i++) {
+ /* omit if already choosen */
+ if (checked[i])
+ continue;
+ /* omit if no BSIC available */
+ if (rrmeas->nc_bsic[i] > 0x3f)
+ continue;
/* only check if NCC is permitted */
ncc = rrmeas->nc_bsic[i] >> 3;
if ((s->nb_ncc_permitted_si6 & (1 << ncc))
- && rrmeas->nc_rxlev_dbm[i] > current
- && rrmeas->nc_rxlev_dbm[i] < strongest) {
+ && rrmeas->nc_rxlev_dbm[i] > current) {
current = rrmeas->nc_rxlev_dbm[i];
index = i;
}
}
if (current == -128) /* no more found */
break;
+ checked[index] = 1;
rxlev_nc[n] = rrmeas->nc_rxlev_dbm[index] + 110;
bsic_nc[n] = rrmeas->nc_bsic[index];
bcch_f_nc[n] = index;
@@ -2862,6 +2939,12 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
mr->rxlev_full - 110, mr->rxlev_sub - 110,
mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used,
(mr->no_nc_n_hi << 2) | mr->no_nc_n_lo);
+ for (i = 0; i < n; i++) {
+ LOGP(DRR, LOGL_INFO, " NB #%d BCCH=%s rxlev=%d "
+ "BSIC=%d.%d\n", i + 1,
+ gsm_print_arfcn(rrmeas->nc_arfcn[bcch_f_nc[i]]),
+ rxlev_nc[i] - 110, bsic_nc[i] >> 3, bsic_nc[i] & 7);
+ }
msgb_tv16_push(nmsg, RSL_IE_L3_INFO,
nmsg->tail - (uint8_t *)msgb_l3(nmsg));
@@ -4042,6 +4125,38 @@ static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause,
return gsm48_send_rsl(ms, rsl_prim, nmsg, 0);
}
+/* get index of measurement result from new cell's BCCH ARFCN */
+static int get_meas_index(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_rr_meas *rrmeas = &rr->meas;
+ int i;
+
+ if (cs->sel_arfcn == rr->hando_new_cell_arfcn) {
+ LOGP(DRR, LOGL_INFO, "Handover to channel of same cell\n");
+ return -1;
+ }
+
+ for (i = 0; i < rrmeas->nc_num; i++) {
+ if (rrmeas->nc_arfcn[i] == rr->hando_new_cell_arfcn) {
+ LOGP(DRR, LOGL_INFO, "Handover to channel of different "
+ "cell\n");
+ return i;
+ }
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "Handover to channel of unknown cell\n");
+ return -EINVAL;
+}
+
+static const char *ho_case[4] = {
+ "Non-synchronized",
+ "Synchronized",
+ "Pre-synchronized",
+ "Pseudo-synchronized",
+};
+
/* receiving HANDOVER COMMAND message (9.1.15) */
static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -4081,9 +4196,20 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
/* cell description */
gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc);
+ /* set new cell arfcn */
+ rr->hando_new_cell_arfcn = arfcn;
+ rr->hando_new_cell_bsic = ncc << 3 | bcc;
+
+ /* get sync information of new cell */
+ rr->hando_meas_index = get_meas_index(ms);
+ if (rr->hando_meas_index == -EINVAL) {
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC);
+ }
+
/* handover reference */
- rr->chan_req_val = ho->ho_ref;
- rr->chan_req_mask = 0x00;
+ rr->hando_ref = ho->ho_ref;
+ LOGP(DRR, LOGL_INFO, " HO-ref: 0x%02x\n", ho->ho_ref);
tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
@@ -4091,8 +4217,13 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
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);
+ LOGP(DRR, LOGL_INFO, " (sync_ind=%s rot=%d nci=%d)\n",
+ ho_case[rr->hando_sync_ind], rr->hando_rot,
+ rr->hando_nci);
+ } else {
+ rr->hando_sync_ind = GSM48_RR_HO_NON_SYNC;
+ LOGP(DRR, LOGL_INFO, " (no sync IE, default sync_ind=%s)\n",
+ ho_case[rr->hando_sync_ind]);
}
/* decode channel description (before time) */
@@ -4302,7 +4433,14 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
(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 */
+ if (rr->hando_sync_ind == GSM48_RR_HO_PRE_SYNC) {
+ if (TLVP_PRESENT(&tp, GSM48_IE_TIMING_ADVANCE)) {
+ uint8_t ta = *TLVP_VAL(&tp, GSM48_IE_TIMING_ADVANCE);
+ cda->ind_ta = cdb->ind_ta = ta;
+ } else
+ cda->ind_ta = cdb->ind_ta = 1; /* default from TS 05.10 */
+ } else
+ cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* use same */
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");
@@ -4381,6 +4519,75 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+static int gsm48_rr_resume_after_handover(struct osmocom_ms *ms)
+{
+ /* get timing advance */
+ LOGP(DRR, LOGL_INFO, "setting new ta and enable TX\n");
+ gsm48_rr_alter_delay(ms);
+
+ /* send DL-RESUME REQUEST */
+ LOGP(DRR, LOGL_INFO, "request resume of data link\n");
+ gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+
+ /* note: keep in HANDOVER modify_state */
+
+ return 0;
+}
+
+/* send HANDOVER ACCESS burst (9.1.14) */
+static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ LOGP(DRR, LOGL_INFO, "send access bursts on DCCH (HO-ref 0x%02x)\n",
+ rr->hando_ref);
+
+ return l1ctl_tx_rach_req(ms, rr->hando_ref, 0, 0);
+}
+
+/* send next channel request in dedicated state */
+static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (rr->modify_state != GSM48_RR_MOD_HANDO) {
+ LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in "
+ "handover state. (no bug)\n");
+ return 0;
+ }
+
+ /* ignore confirm, if physical info is already received */
+ if (!rr->hando_acc_pending) {
+ return 0;
+ }
+
+ /* non-synchrionized: start timer after sending first acccess burst */
+ if (rr->hando_sync_ind == GSM48_RR_HO_NON_SYNC
+ && !osmo_timer_pending(&rr->t3124)) {
+ uint8_t ch_type, ch_subch, ch_ts;
+ rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch,
+ &ch_ts);
+ if (ch_type == RSL_CHAN_SDCCH4_ACCH
+ || ch_type == RSL_CHAN_SDCCH8_ACCH)
+ start_rr_t3124(rr, GSM_T3124_675);
+ else
+ start_rr_t3124(rr, GSM_T3124_320);
+ }
+
+ /* count down for sync handover */
+ if (rr->hando_sync_ind != GSM48_RR_HO_NON_SYNC)
+ rr->hando_acc_pending--;
+
+ /* send more access bursts */
+ if (rr->hando_acc_pending) {
+ gsm48_rr_tx_hando_access(ms);
+ return 0;
+ }
+
+ /* in case of any sync handover, resume after 4 access bursts */
+ return gsm48_rr_resume_after_handover(ms);
+}
+
/* send all queued messages down to layer 2 */
static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
{
@@ -4410,6 +4617,12 @@ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DRR, LOGL_INFO, "data link is resumed\n");
+ /* if we handovered to a new cell, update info about current cell */
+ if (rr->modify_state == GSM48_RR_MOD_HANDO) {
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ cs->sel_arfcn = rr->hando_new_cell_arfcn;
+ }
+
/* transmit queued frames during ho / ass transition */
gsm48_rr_dequeue_down(ms);
@@ -4426,6 +4639,8 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
if (rr->modify_state) {
uint16_t ma[64];
uint8_t ma_len;
+ int enable_tx = (rr->modify_state != GSM48_RR_MOD_HANDO);
+ int sync = (rr->modify_state == GSM48_RR_MOD_HANDO);
/* deactivating dedicated mode */
LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated "
@@ -4460,31 +4675,61 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
/* activate channel */
- gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len,
+ enable_tx, sync, rr->hando_meas_index);
}
- /* send DL-RESUME REQUEST */
- LOGP(DRR, LOGL_INFO, "request resume of data link\n");
switch (rr->modify_state) {
case GSM48_RR_MOD_ASSIGN:
+ /* send DL-RESUME REQUEST */
+ LOGP(DRR, LOGL_INFO, "request resume of data link\n");
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;
- }
-
-#ifdef TODO
- /* trigger RACH */
- if (rr->modify_state == GSM48_RR_MOD_HANDO) {
+ /* trigger Handover access bursts */
+ /* NOTE: T3124 is started after RACH CONF */
gsm48_rr_tx_hando_access(ms);
- rr->hando_acc_left = 3;
+ rr->hando_acc_pending = 4;
+ break;
}
-#endif
}
return 0;
}
+static int gsm48_rr_rx_phys_info(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t *ind_ta = (uint8_t *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ind_ta);
+
+ LOGP(DRR, LOGL_INFO, "PHYSICAL INFORMATION\n");
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of PHYSICAL INFORMATION "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ /* ignore physical info, if not handover */
+ if (rr->modify_state != GSM48_RR_MOD_HANDO)
+ return 0;
+
+ /* ignore pyhsical info, if already received (no more bursts) */
+ if (rr->hando_acc_pending == 0)
+ return 0;
+
+ rr->cd_now.ind_ta = *ind_ta;
+
+ /* stop access bursts */
+ rr->hando_acc_pending = 0;
+
+ /* stop timer */
+ stop_rr_t3124(rr);
+
+ return gsm48_rr_resume_after_handover(ms);
+}
+
/*
* radio ressource requests
*/
@@ -4647,8 +4892,10 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4;
/* ignore if skip indicator is not B'0000' */
- if (skip_ind)
+ if (skip_ind) {
+ msgb_free(msg);
return 0;
+ }
switch(gh->msg_type) {
case GSM48_MT_RR_ADD_ASS:
@@ -4756,8 +5003,8 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
}
}
-/* receive ACCH at RR layer */
-static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
+/* receive ACCH unit data at RR layer */
+static int gsm48_rr_rx_acch_unit(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
struct gsm_settings *set = &ms->settings;
@@ -4802,6 +5049,33 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
}
}
+/* receive DCCH unit data at RR layer */
+static int gsm48_rr_rx_dcch_unit(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+
+ /* FIXME: set correct L3 message length */
+
+ if (pdisc == GSM48_PDISC_RR) {
+ uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4;
+
+ /* ignore if skip indicator is not B'0000' */
+ if (skip_ind)
+ return 0;
+
+ switch(gh->msg_type) {
+ case GSM48_MT_RR_HANDO_INFO:
+ return gsm48_rr_rx_phys_info(ms, msg);
+ default:
+ LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
+ gh->msg_type);
+ }
+ }
+
+ return 0;
+}
+
/* unit data from layer 2 to RR layer */
static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -4857,7 +5131,10 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
case RSL_CHAN_Lm_ACCHs:
case RSL_CHAN_SDCCH4_ACCH:
case RSL_CHAN_SDCCH8_ACCH:
- return gsm48_rr_rx_acch(ms, msg);
+ if ((rllh->link_id & 0x40))
+ return gsm48_rr_rx_acch_unit(ms, msg);
+ else
+ return gsm48_rr_rx_dcch_unit(ms, msg);
default:
LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
rllh->chan_nr);
@@ -4905,6 +5182,37 @@ static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+/* function to return to last channel */
+static int gsm48_rr_as_ho_failed(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint16_t ma[64];
+ uint8_t ma_len;
+
+ /* deactivate channel */
+ l1ctl_tx_dm_rel_req(ms);
+ ms->meas.rl_fail = 0;
+ rr->dm_est = 0;
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
+ /* 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, 1, 1, -1);
+
+ if (rr->modify_state == GSM48_RR_MOD_ASSIGN) {
+ rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
+ return gsm48_rr_tx_ass_fail(ms,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC, RSL_MT_RECON_REQ);
+ } else {
+ rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
+ return gsm48_rr_tx_hando_fail(ms,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC, RSL_MT_RECON_REQ);
+ }
+}
+
/* release confirm */
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -4912,42 +5220,12 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
struct gsm48_rr_hdr *nrrh;
uint8_t cause = RR_REL_CAUSE_NORMAL;
- uint16_t ma[64];
- uint8_t ma_len;
/* switch back to old channel, if modify/ho failed */
switch (rr->modify_state) {
case GSM48_RR_MOD_ASSIGN:
case GSM48_RR_MOD_HANDO:
- /* deactivate channel */
- l1ctl_tx_dm_rel_req(ms);
- ms->meas.rl_fail = 0;
- rr->dm_est = 0;
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
-
- /* 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;
- if (rr->modify_state == GSM48_RR_MOD_ASSIGN) {
- rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
- return gsm48_rr_tx_ass_fail(ms,
- GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
- RSL_MT_RECON_REQ);
- } else {
- rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
- return gsm48_rr_tx_hando_fail(ms,
- GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
- RSL_MT_RECON_REQ);
- }
- /* returns above */
+ return gsm48_rr_as_ho_failed(ms);
case GSM48_RR_MOD_ASSIGN_RESUME:
case GSM48_RR_MOD_HANDO_RESUME:
rr->modify_state = GSM48_RR_MOD_NONE;
@@ -5265,10 +5543,6 @@ static struct dldatastate {
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
-#if 0
- {SBIT(GSM48_RR_ST_DEDICATED),
- RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
-#endif
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
@@ -5373,6 +5647,13 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
return rc;
}
+ if (rr->state == GSM48_RR_ST_DEDICATED
+ && msg_type == RSL_MT_CHAN_CONF) {
+ rc = gsm48_rr_rand_acc_cnf_dedicated(ms, msg);
+ msgb_free(msg);
+ return rc;
+ }
+
LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
msgb_free(msg);
return 0;
@@ -5550,86 +5831,6 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
return 0;
}
-#if 0
-
-todo rr_sync_ind when receiving ciph, re ass, channel mode modify
-
-
-static void timeout_rr_t3124(void *arg)
-{
- struct gsm48_rrlayer *rr = arg;
- struct msgb *nmsg;
-
- /* stop sending more access bursts when timer expired */
- hando_acc_left = 0;
-
- /* get old channel description */
- 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,
- rr->cd_now.tsc);
- rr->dm_est = 1;
-
- /* re-establish old link */
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg, 0);
-
- todo
-}
-
-/* send HANDOVER ACCESS burst (9.1.14) */
-static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms)
-{
- nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS");
- if (!nmsg)
- return -ENOMEM;
- *msgb_put(nmsg, 1) = rr->hando_ref;
- todo burst
- return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg, 0);
-}
-
-/* send next channel request in dedicated state */
-static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm48_rrlayer *rr = &ms->rrlayer;
- struct msgb *nmsg;
- int s;
-
- if (rr->modify_state != GSM48_RR_MOD_HANDO) {
- LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n");
- return 0;
- }
-
- /* send up to four handover access bursts */
- if (rr->hando_acc_left) {
- rr->hando_acc_left--;
- gsm48_rr_tx_hando_access(ms);
- return;
- }
-
- /* start timer for sending next HANDOVER ACCESS bursts afterwards */
- if (!osmo_timer_pending(&rr->t3124)) {
- if (allocated channel is SDCCH)
- start_rr_t3124(rr, GSM_T3124_675);
- else
- start_rr_t3124(rr, GSM_T3124_320);
- }
- if (!rr->n_chan_req) {
- start_rr_t3126(rr, 5, 0); /* TODO improve! */
- return 0;
- }
- rr->n_chan_req--;
-
- /* wait for PHYSICAL INFORMATION message or T3124 timeout */
- return 0;
-
-}
-
-#endif
-
int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;