From 8f56258867ed321f8aee59f8cd0fb6a1f36090c9 Mon Sep 17 00:00:00 2001 From: Muhammad Awais Aslam Date: Tue, 19 Dec 2017 15:57:20 +0500 Subject: WIP/HACK: bsic decoding of neighbour cells in dedicated mode and partially success in synchronized handover Change-Id: Ib01460b796d2107c4599d327e184eb42340999d2 --- include/l1ctl_proto.h | 3 +- src/host/layer23/include/osmocom/bb/common/l1ctl.h | 2 +- src/host/layer23/src/common/l1ctl.c | 3 +- src/host/layer23/src/common/l1ctl_lapdm_glue.c | 2 +- src/host/layer23/src/misc/app_cbch_sniff.c | 4 +- src/host/layer23/src/mobile/gsm48_rr.c | 906 ++++++++---- src/target/firmware/Makefile | 2 +- src/target/firmware/Makefile.mtk | 10 +- src/target/firmware/include/layer1/async.h | 2 +- src/target/firmware/include/layer1/mframe_sched.h | 2 + src/target/firmware/include/layer1/prim.h | 8 +- src/target/firmware/include/layer1/sync.h | 42 +- src/target/firmware/include/layer1/tpu_window.h | 2 + src/target/firmware/layer1/afc.c | 5 +- src/target/firmware/layer1/l23_api.c | 62 +- src/target/firmware/layer1/mframe_sched.c | 63 +- src/target/firmware/layer1/prim_fbsb.c | 642 +++++++- .../prim_fbsbSynchUsingDedModeDSPTasksInIdleCopy.c | 1559 ++++++++++++++++++++ src/target/firmware/layer1/prim_fbsbcurr.c | 1223 +++++++++++++++ src/target/firmware/layer1/prim_pm.c | 192 ++- src/target/firmware/layer1/prim_rach.c | 121 +- src/target/firmware/layer1/prim_rach_tmp.c | 265 ++++ src/target/firmware/layer1/prim_rx_nb.c | 4 + src/target/firmware/layer1/prim_tch.c | 35 +- src/target/firmware/layer1/prim_tx_nb.c | 1 + src/target/firmware/layer1/sync.c | 33 + src/target/firmware/layer1/tmpfbsb.c | 1017 +++++++++++++ src/target/firmware/layer1/tmpfbsbWorking.c | 937 ++++++++++++ src/target/firmware/layer1/tpu_window.c | 15 +- src/target/firmware/prim_fbsbwithFB1.c | 1243 ++++++++++++++++ src/target/firmware/prim_fbsbwithTotal.c | 1188 +++++++++++++++ 31 files changed, 9253 insertions(+), 340 deletions(-) create mode 100644 src/target/firmware/layer1/prim_fbsbSynchUsingDedModeDSPTasksInIdleCopy.c create mode 100644 src/target/firmware/layer1/prim_fbsbcurr.c create mode 100644 src/target/firmware/layer1/prim_rach_tmp.c create mode 100644 src/target/firmware/layer1/tmpfbsb.c create mode 100644 src/target/firmware/layer1/tmpfbsbWorking.c create mode 100644 src/target/firmware/prim_fbsbwithFB1.c create mode 100644 src/target/firmware/prim_fbsbwithTotal.c diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index 05d65deb..deef07bc 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -174,7 +174,8 @@ struct l1ctl_info_ul { uint8_t chan_nr; /* GSM 08.58 link identifier (9.3.2) */ uint8_t link_id; - uint8_t padding[2]; + /* the ARFCN and the band */ + uint16_t band_arfcn; uint8_t payload[0]; } __attribute__((packed)); diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h index e4dbdedc..f99d8501 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -21,7 +21,7 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, /* Transmit L1CTL_RACH_REQ */ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, - uint8_t combined); + uint8_t combined, uint16_t arfcn); /* Transmit L1CTL_DM_EST_REQ */ int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index 5d6d9c0c..ccee15e3 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -432,7 +432,7 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, /* Transmit L1CTL_RACH_REQ */ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, - uint8_t combined) + uint8_t combined, uint16_t band_arfcn) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -444,6 +444,7 @@ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, DEBUGP(DL1C, "RACH Req. offset=%d combined=%d\n", offset, combined); ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->band_arfcn = htons(band_arfcn); //MTZ - Added req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req)); req->ra = ra; req->offset = htons(offset); diff --git a/src/host/layer23/src/common/l1ctl_lapdm_glue.c b/src/host/layer23/src/common/l1ctl_lapdm_glue.c index 0b2a8ed5..2f352c38 100644 --- a/src/host/layer23/src/common/l1ctl_lapdm_glue.c +++ b/src/host/layer23/src/common/l1ctl_lapdm_glue.c @@ -52,7 +52,7 @@ int l1ctl_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx) pp->u.rach_req.tx_power); rc = l1ctl_tx_rach_req(ms, pp->u.rach_req.ra, pp->u.rach_req.offset, - pp->u.rach_req.is_combined_ccch); + pp->u.rach_req.is_combined_ccch, 0); break; default: rc = -EINVAL; diff --git a/src/host/layer23/src/misc/app_cbch_sniff.c b/src/host/layer23/src/misc/app_cbch_sniff.c index 8256eaf6..20e730f5 100644 --- a/src/host/layer23/src/misc/app_cbch_sniff.c +++ b/src/host/layer23/src/misc/app_cbch_sniff.c @@ -57,12 +57,12 @@ static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s) return l1ctl_tx_dm_est_req_h1(ms, s->maio, s->hsn, s->hopping, s->hopp_len, s->chan_nr, s->tsc, - GSM48_CMODE_SIGN, 0); + GSM48_CMODE_SIGN, 0, 1, 0, 0); } else { LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n", s->chan_nr, s->tsc, s->arfcn); return l1ctl_tx_dm_est_req_h0(ms, s->arfcn, - s->chan_nr, s->tsc, GSM48_CMODE_SIGN, 0); + s->chan_nr, s->tsc, GSM48_CMODE_SIGN, 0, 1, 0, 0); } } diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index c074323f..848abbb7 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -92,6 +92,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 @@ -147,13 +148,11 @@ static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len, *ranges = 0; return -EINVAL; } - - if (n > max_ranges) { + if (max_ranges > n) LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number " "of ranges supported by this mobile (%d).\n", n, max_ranges); n = max_ranges; - } /* decode ranges */ for (i = 0; i < n; i++) { @@ -365,6 +364,8 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state) stop_rr_t_starting(rr); /* stop handover timer */ stop_rr_t3124(rr); + /* stop faking measurement report */ + rr->hando_fake_report = 0; } rr->state = state; @@ -526,6 +527,7 @@ static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type, } rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr, link_id, 1); + printf("MTZ: lapdm_rslms_recvmsg called 1 (in gsm48_send_rsl)\n"); return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel); } @@ -538,12 +540,14 @@ static int gsm48_send_rsl_nol3(struct osmocom_ms *ms, uint8_t msg_type, rsl_rll_push_hdr(msg, msg_type, rr->cd_now.chan_nr, link_id, 1); + printf("MTZ: lapdm_rslms_recvmsg called 2 (in gsm48_send_rsl_nol3)\n"); return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel); } /* enqueue messages (RSL-SAP) */ static int rcv_rsl(struct msgb *msg, struct lapdm_entity *le, void *l3ctx) { + printf("MTZ: In rcv_rsl\n"); struct osmocom_ms *ms = l3ctx; struct gsm48_rrlayer *rr = &ms->rrlayer; @@ -644,8 +648,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); @@ -744,6 +749,16 @@ static void timeout_rr_t3122(void *arg) LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n"); } +static void timeout_rr_t3124(void *arg) +{ + 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) { struct gsm48_rrlayer *rr = arg; @@ -808,6 +823,15 @@ static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro) osmo_timer_schedule(&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; + osmo_timer_schedule(&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.%03d seconds\n", sec, @@ -1288,7 +1312,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, struct gsm_settings *set = &ms->settings; struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; uint8_t chan_req_val, chan_req_mask; @@ -1341,6 +1365,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, /* generate CHAN REQ (9.1.8) */ switch (cause) { case RR_EST_CAUSE_EMERGENCY: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_EMERGENCY\n"); /* 101xxxxx */ chan_req_mask = 0x1f; chan_req_val = 0xa0; @@ -1348,12 +1373,14 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, chan_req_val); break; case RR_EST_CAUSE_REESTAB_TCH_F: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_REESTAB_TCH_F\n"); chan_req_mask = 0x1f; chan_req_val = 0xc0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish " "TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_REESTAB_TCH_H: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_REESTAB_TCH_H\n"); if (s->neci) { chan_req_mask = 0x03; chan_req_val = 0x68; @@ -1368,6 +1395,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, } break; case RR_EST_CAUSE_REESTAB_2_TCH_H: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_REESTAB_2_TCH_H\n"); if (s->neci) { chan_req_mask = 0x03; chan_req_val = 0x6c; @@ -1383,18 +1411,21 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, } break; case RR_EST_CAUSE_ANS_PAG_ANY: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_ANS_PAG_ANY\n"); chan_req_mask = 0x1f; chan_req_val = 0x80; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING " "Any channel)\n", chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_SDCCH: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_ANS_PAG_SDCCH\n"); chan_req_mask = 0x0f; chan_req_val = 0x10; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n", chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_TCH_F: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_ANS_PAG_TCH_F\n"); switch (set->ch_cap) { case GSM_CAP_SDCCH: chan_req_mask = 0x0f; @@ -1413,6 +1444,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_TCH_ANY: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_ANS_PAG_TCH_ANY\n"); switch (set->ch_cap) { case GSM_CAP_SDCCH: chan_req_mask = 0x0f; @@ -1431,6 +1463,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, "TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_ORIG_TCHF: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_ORIG_TCHF\n"); /* ms supports no dual rate */ chan_req_mask = 0x1f; chan_req_val = 0xe0; @@ -1438,6 +1471,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, chan_req_val); break; case RR_EST_CAUSE_LOC_UPD: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_LOC_UPD\n"); if (s->neci) { chan_req_mask = 0x0f; chan_req_val = 0x00; @@ -1451,6 +1485,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, } break; case RR_EST_CAUSE_OTHER_SDCCH: + printf("MTZ: In gsm48_rr_chan_req - RR_EST_CAUSE_OTHER_SDCCH\n"); if (s->neci) { chan_req_mask = 0x0f; chan_req_val = 0x10; @@ -1464,6 +1499,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, } break; default: + printf("MTZ: In gsm48_rr_chan_req - Unknown\n"); if (!rr->rr_est_req) /* no request from MM */ return -EINVAL; @@ -1498,8 +1534,12 @@ rel_ind: rr->paging_mi_type = paging_mi_type; /* if channel is already active somehow */ - if (cs->ccch_state == GSM322_CCCH_ST_DATA) + if (cs->ccch_state == GSM322_CCCH_ST_DATA){ + printf("MTZ: gsm48_rr_tx_rand_acc called 1 (in gsm48_rr_chan_req)\n"); return gsm48_rr_tx_rand_acc(ms, NULL); + } else { + printf("MTZ: gsm48_rr_tx_rand_acc not sent as cs->ccch_state not GSM322_CCCH_ST_DATA\n"); + } return 0; } @@ -1517,6 +1557,8 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) uint8_t chan_req; uint8_t tx_power; + printf("MTZ: In gsm48_rr_tx_rand_acc\n"); + /* already assigned */ if (rr->wait_assign == 2) return 0; @@ -1658,22 +1700,22 @@ fail: tx_power = set->alter_tx_power_value; LOGP(DRR, LOGL_INFO, "Use alternative tx-power %d (%d dBm)\n", tx_power, - ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power)); + ms_pwr_dbm(gsm_arfcn2band(cs->sel_arfcn), tx_power)); } else { tx_power = s->ms_txpwr_max_cch; /* power offset in case of DCS1800 */ - if (s->po && (cs->arfcn & 1023) >= 512 - && (cs->arfcn & 1023) <= 885) { + if (s->po && (cs->sel_arfcn & 1023) >= 512 + && (cs->sel_arfcn & 1023) <= 885) { LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " "%d (%d dBm) with offset %d dBm\n", tx_power, - ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power), - s->po_value * 2); + ms_pwr_dbm(gsm_arfcn2band(cs->sel_arfcn), + tx_power), s->po_value * 2); /* use reserved bits 7,8 for offset (+ X * 2dB) */ tx_power |= s->po_value << 6; } else LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " "%d (%d dBm)\n", tx_power, - ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), + ms_pwr_dbm(gsm_arfcn2band(cs->sel_arfcn), tx_power)); } ncch->data[7] = tx_power; @@ -1685,9 +1727,112 @@ fail: /* store ra until confirmed, then copy it with time into cr_hist */ rr->cr_ra = chan_req; + printf("MTZ: lapdm_rslms_recvmsg called 3 (in gsm48_rr_tx_rand_acc)\n"); return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel); } +/* + * 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 gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + 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->sel_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, "\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; + } + + //struct gsm322_cellsel *cs = &ms->cellsel; + + //if (cs->sel_arfcn == 25) { + // if (rrmeas->nc_arfcn[i] == 57) { + // rrmeas->nc_rxlev_dbm[i] = -47; + // rrmeas->nc_bsic[i] = 57; + // } + //} else { + // if (rrmeas->nc_arfcn[i] == 25) { + // rrmeas->nc_rxlev_dbm[i] = -47; + // rrmeas->nc_bsic[i] = 27; + // } + //} + } + + return 0; +} + /* * system information */ @@ -1695,45 +1840,28 @@ fail: /* send sysinfo event to other layers */ 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 gsm48_sysinfo *s = &cs->sel_si; struct msgb *nmsg; struct gsm322_msg *em; /* update list of measurements, if BA(SACCH) is complete and new */ - if (s - && (type == GSM48_MT_RR_SYSINFO_5 + if ((type == GSM48_MT_RR_SYSINFO_5 || type == GSM48_MT_RR_SYSINFO_5bis || 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); + } + + /* update stuff from SI 6 */ + if (type == GSM48_MT_RR_SYSINFO_6 && s->si6) { + cs->sel_mcc = s->mcc; + cs->sel_mnc = s->mnc; + cs->sel_lac = s->lac; + cs->sel_id = s->cell_id; } /* send sysinfo event to other layers */ @@ -1914,6 +2042,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 4" message (9.1.36) */ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) { + /* NOTE: pseudo length is not in this structure, so we skip */ struct gsm48_system_information_type_4 *si = msgb_l3(msg); struct gsm48_sysinfo *s = ms->cellsel.si; int payload_len = msgb_l3len(msg) - sizeof(*si); @@ -1945,9 +2074,11 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5" message (9.1.37) */ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5 *si = msgb_l3(msg); - struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1; + /* Note: Only store SI5* / SI6 in temporary sel_si */ + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " @@ -1974,9 +2105,11 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5bis *si = msgb_l3(msg); - struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1; + /* Note: Only store SI5* / SI6 in temporary sel_si */ + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" @@ -2004,9 +2137,11 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5ter *si = msgb_l3(msg); - struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1; + /* Note: Only store SI5* / SI6 in temporary sel_si */ + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" @@ -2034,10 +2169,12 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 6" message (9.1.39) */ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_6 *si = msgb_l3(msg); - struct gsm48_sysinfo *s = ms->cellsel.si; + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1; + /* Note: Only store SI5* / SI6 in temporary sel_si */ + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; struct rx_meas_stat *meas = &ms->meas; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " @@ -2163,9 +2300,11 @@ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) mi = pa->data; if (payload_len < mi[0] + 1) goto short_read; - if ((mi_type = gsm_match_mi(ms, mi)) > 0) + if ((mi_type = gsm_match_mi(ms, mi)) > 0){ + printf("MTZ: gsm48_rr_chan_req called 1 (in gsm48_rr_rx_pag_req_1)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, mi_type); + } /* second MI */ payload_len -= mi[0] + 1; mi = pa->data + mi[0] + 1; @@ -2175,9 +2314,11 @@ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) return 0; if (payload_len < mi[1] + 2) goto short_read; - if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0) + if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0){ + printf("MTZ: gsm48_rr_chan_req called 2 (in gsm48_rr_rx_pag_req_1)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, mi_type); + } return 0; } @@ -2220,6 +2361,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); + printf("MTZ: gsm48_rr_chan_req called 3 (in gsm48_rr_rx_pag_req_2)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); } else @@ -2231,6 +2373,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); + printf("MTZ: gsm48_rr_chan_req called 4 (in gsm48_rr_rx_pag_req_2)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); } else @@ -2245,9 +2388,10 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */ goto short_read; chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */ - if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0) + if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0){ + printf("MTZ: gsm48_rr_chan_req called 5 (in gsm48_rr_rx_pag_req_2)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1, - mi_type); + mi_type);} return 0; } @@ -2290,6 +2434,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); + printf("MTZ: gsm48_rr_chan_req called 6 (in gsm48_rr_rx_pag_req_3)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); } else @@ -2301,6 +2446,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); + printf("MTZ: gsm48_rr_chan_req called 7 (in gsm48_rr_rx_pag_req_3)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); } else @@ -2312,6 +2458,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3)); + printf("MTZ: gsm48_rr_chan_req called 8 (in gsm48_rr_rx_pag_req_3)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1, GSM_MI_TYPE_TMSI); } else @@ -2323,6 +2470,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) && ms->subscr.mnc == cs->sel_mnc && ms->subscr.lac == cs->sel_lac) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4)); + printf("MTZ: gsm48_rr_chan_req called 9 (in gsm48_rr_rx_pag_req_3)\n"); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1, GSM_MI_TYPE_TMSI); } else @@ -2378,7 +2526,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_imm_ass *ia = msgb_l3(msg); struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; int ma_len = msgb_l3len(msg) - sizeof(*ia); uint8_t ch_type, ch_subch, ch_ts; struct gsm48_rr_cd cd; @@ -2426,6 +2574,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) cd.chan_nr = ia->chan_desc.chan_nr; rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); if (ia->chan_desc.h0.h) { + printf("\nMTZ: ia->chan_desc.h0.h = 1\n"); cd.h = 1; gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, &cd.hsn); @@ -2436,9 +2585,10 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); } else { + printf("\nMTZ: ia->chan_desc.h0.h = 0\n"); cd.h = 0; gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) cd.arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", @@ -2485,7 +2635,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct gsm48_imm_ass_ext *ia = msgb_l3(msg); int ma_len = msgb_l3len(msg) - sizeof(*ia); uint8_t ch_type, ch_subch, ch_ts; @@ -2552,7 +2702,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd1.h = 0; gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) cd1.arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", @@ -2576,7 +2726,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd2.h = 0; gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) cd2.arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", @@ -2710,7 +2860,7 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm48_sysinfo *s = ms->cellsel.si; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; struct rx_meas_stat *meas = &rr->ms->meas; struct gsm48_rr_meas *rrmeas = &rr->meas; struct msgb *nmsg; @@ -2719,8 +2869,8 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0, serv_rxqual_sub = 0; uint8_t ta, tx_power; - uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0; - uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6]; + uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0, multi_rep = 0; + uint8_t n = 0, i, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6]; /* just in case! */ if (!s) @@ -2745,41 +2895,67 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) (meas->rxlev + (meas->frames / 2)) / meas->frames; serv_rxqual_full = serv_rxqual_sub = 0; // FIXME } + + /* fake serving cell, if wanted */ + if (rr->hando_fake_report) { + struct gsm322_cellsel *cs = &ms->cellsel; + + if (rr->hando_fake_report_arfcn == cs->sel_arfcn) + serv_rxlev_full = serv_rxlev_sub = 63; /* -47 dBm */ + else + serv_rxlev_full = serv_rxlev_sub = 10; /* -100 dBm */ + serv_rxqual_full = serv_rxqual_sub = 0; // quality is ok + } memset(&rxlev_nc, 0, sizeof(rxlev_nc)); 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]; -#if 0 - /* FIXME: multi-band reporting, if not: 0 = normal reporting */ - uint8_t multi_rep = s->si5ter ? - s->nb_multi_rep_si5ter : 0; -#endif + /* multiband reporting, if not: 0 = normal reporting */ + if (s->si5ter) + multi_rep = s->nb_multi_rep_si5ter; /* get 6 strongest measurements */ - strongest = 127; /* infinite */ + // FIXME: multiband report + 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; + + /* fake neighbor cell, if wanted */ + if (rr->hando_fake_report) { + if (rr->hando_fake_report_arfcn + == rrmeas->nc_arfcn[index]) + rxlev_nc[n] = 63; /* -47 dBm */ + else + rxlev_nc[n] = 10; /* -100 dBm */ + } } } @@ -2849,6 +3025,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)); @@ -2860,6 +3042,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr, 0x40, 1); + printf("MTZ: lapdm_rslms_recvmsg called 4 (in gsm48_rr_tx_meas_rep)\n"); return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel); } @@ -2936,20 +3119,27 @@ int gsm48_rr_los(struct osmocom_ms *ms) /* activation of channel in dedicated mode */ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, - struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len) + struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, int tx, + int sync, int index) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm_settings *set = &ms->settings; - struct gsm48_sysinfo *s = ms->cellsel.si; - struct rx_meas_stat *meas = &ms->meas; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + struct rx_meas_stat *meas = &ms->meas; uint8_t ch_type, ch_subch, ch_ts; uint8_t timeout = 64; + int ta = cd->ind_ta; /* 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); - l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay, + if (!tx) { + ta = 0; + LOGP(DRR, LOGL_INFO, "setting TA 0 for access bursts " + "(actual TA %d)\n", ta - set->alter_delay); + } else + LOGP(DRR, LOGL_INFO, "setting indicated TA %d " + "(actual TA %d)\n", cd->ind_ta, ta - set->alter_delay); + l1ctl_tx_param_req(ms, ta - set->alter_delay, (set->alter_tx_power) ? set->alter_tx_power_value : cd->ind_tx_power); @@ -2984,15 +3174,17 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n"); rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, " - "audio-mode %d, cipher %d\n", ch_type, ch_subch, ch_ts, - cd->mode, rr->audio_mode, rr->cipher_type + 1); + "audio-mode %d, cipher %d, tsc %d, ARFCN %s, hopping=%s\n", + ch_type, ch_subch, ch_ts, cd->mode, rr->audio_mode, + rr->cipher_type + 1, cd->tsc, gsm_print_arfcn(cd->arfcn), + (cd->h) ? "yes":"no"); if (cd->h) l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->chan_nr, cd->tsc, cd->mode, - rr->audio_mode); + rr->audio_mode, tx, sync, index); else l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, - cd->mode, rr->audio_mode); + cd->mode, rr->audio_mode, tx, sync, index); rr->dm_est = 1; /* old SI 5/6 are not valid on a new dedicated channel */ @@ -3032,12 +3224,12 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct gsm_settings *set = &ms->settings; int i, pcs, index; uint16_t arfcn; - pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0; + pcs = gsm_refer_pcs(cs->sel_arfcn, s) ? ARFCN_PCS : 0; /* no hopping (no MA), channel description is valid */ if (!cd->h) { @@ -3262,7 +3454,8 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms) rr->cd_before.h = 0; rr->cd_before.arfcn = 0; /* activate channel */ - gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len); + printf("MTZ: gsm48_rr_activate_channel called 1\n"); + gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len, 1, 0, 0); /* render channel "after time" */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); /* schedule change of channel */ @@ -3270,7 +3463,8 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms) rr->cd_now.start_tm.fn); #else /* activate channel */ - gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); + printf("MTZ: gsm48_rr_activate_channel called 2\n"); + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len, 1, 0, 0); #endif /* set T200 of SAPI 0 */ @@ -3443,7 +3637,7 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; 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; @@ -3480,7 +3674,7 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) } else { cd.h = 0; gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) cd.arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n", gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); @@ -3553,7 +3747,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_chan_mode_modify *cm = (struct gsm48_chan_mode_modify *)gh->data; @@ -3585,7 +3779,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) } else { cd->h = 0; gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) cd->arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u " "TSC %u mode %u)\n", cm->chan_desc.chan_nr, @@ -3661,7 +3855,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac); @@ -3708,7 +3902,8 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) + printf("\nMTZ: Orig with ARFCN_PCS\n"); cdb->arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, @@ -3731,7 +3926,8 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) + printf("\nMTZ: Orig with ARFCN_PCS\n"); cda->arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ac->chan_desc.chan_nr, @@ -4028,12 +4224,44 @@ 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 - %d (serving cell - %d)\n", rr->hando_new_cell_arfcn, cs->sel_arfcn); + 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) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm48_sysinfo *s = cs->si; + struct gsm48_sysinfo *s = &cs->sel_si; 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); @@ -4050,7 +4278,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) uint8_t cause; struct msgb *nmsg; - LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n"); + LOGP(DRR, LOGL_INFO, "\n\n------------------\nHANDOVER COMMAND\n----------------\n\n"); memset(cda, 0, sizeof(*cda)); cda->ind_tx_power = rr->cd_now.ind_tx_power; @@ -4067,9 +4295,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); @@ -4077,8 +4316,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) */ @@ -4097,8 +4341,10 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) { + printf("\nMTZ: Orig with ARFCN_PCS\n"); cdb->arfcn |= ARFCN_PCS; + } LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, gsm_print_arfcn(cdb->arfcn), @@ -4120,7 +4366,8 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) + if (gsm_refer_pcs(cs->sel_arfcn, s)) + printf("\nMTZ: Orig with ARFCN_PCS\n"); cda->arfcn |= ARFCN_PCS; LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ho->chan_desc.chan_nr, @@ -4288,7 +4535,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"); @@ -4367,6 +4621,79 @@ 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); + + printf("\nMTZ: handover new arfcn = %d (%s with the or)\n", rr->hando_new_cell_arfcn, gsm_print_arfcn(rr->hando_new_cell_arfcn | ARFCN_PCS)); + + //return l1ctl_tx_rach_req(ms, rr->hando_ref, 0, 0); + return l1ctl_tx_rach_req(ms, rr->hando_ref, 0, 0, rr->hando_new_cell_arfcn); //MTZ - modified +} + +/* 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) { + printf("MTZ - gsm48_rr_tx_hando_access call 1\n"); + 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) { @@ -4396,6 +4723,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); @@ -4412,6 +4745,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 " @@ -4432,8 +4767,9 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len); /* activate channel */ + printf("MTZ: gsm48_rr_activate_channel called 3\n"); gsm48_rr_activate_channel(ms, &rr->cd_before, ma, - ma_len); + ma_len, enable_tx, sync, rr->hando_meas_index); /* render channel "after time" */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); @@ -4446,29 +4782,86 @@ 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); + printf("MTZ: gsm48_rr_activate_channel called 4\n"); + 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); + /* trigger Handover access bursts */ + /* NOTE: T3124 is started after RACH CONF */ + printf("MTZ - gsm48_rr_tx_hando_access call 2\n"); + gsm48_rr_tx_hando_access(ms); + rr->hando_acc_pending = 4; break; } + } + return 0; +} -#ifdef TODO - /* trigger RACH */ - if (rr->modify_state == GSM48_RR_MOD_HANDO) { - gsm48_rr_tx_hando_access(ms); - rr->hando_acc_left = 3; +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"); + printf("\n\n----------------------------\nPHYSICAL INFORMATION\n--------------------------\n\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); +} + +/* force handover to given ARFCN, by faking measurement report */ +const char *gsm48_rr_force_handover(struct osmocom_ms *ms, uint16_t arfcn) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_rr_meas *rrmeas = &rr->meas; + int i; + + if (rr->state != GSM48_RR_ST_DEDICATED) + return "No dedicated connection"; + + if (rrmeas->nc_num == 0) + return "No Neighbor cells given by BTS"; + + for (i = 0; i < rrmeas->nc_num; i++) { + if (rrmeas->nc_arfcn[i] == arfcn) { + rr->hando_fake_report = 1; + rr->hando_fake_report_arfcn = arfcn; + return 0; } -#endif } - return 0; + + return "Given ARFCN is not a neighbor cell"; } /* @@ -4577,6 +4970,7 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) msgb_l3(msg), msgb_l3len(msg)); /* request channel */ + printf("MTZ: gsm48_rr_chan_req called 10 (in gsm48_rr_est_req)\n"); return gsm48_rr_chan_req(ms, rrh->cause, 0, 0); } @@ -4633,38 +5027,50 @@ 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: + printf("MTZ: GSM48_MT_RR_ADD_ASS, calling gsm48_rr_rx_add_ass (in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_add_ass(ms, msg); break; case GSM48_MT_RR_ASS_CMD: + printf("MTZ: GSM48_MT_RR_ASS_CMD, calling gsm48_rr_rx_ass_cmd(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_ass_cmd(ms, msg); break; case GSM48_MT_RR_CIPH_M_CMD: + printf("MTZ: GSM48_MT_RR_CIPH_M_CMD, calling gsm48_rr_rx_cip_mode_cmd(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_cip_mode_cmd(ms, msg); break; case GSM48_MT_RR_CLSM_ENQ: + printf("MTZ: GSM48_MT_RR_CLSM_ENQ, calling gsm48_rr_rx_cm_enq(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_cm_enq(ms, msg); break; case GSM48_MT_RR_CHAN_MODE_MODIF: + printf("MTZ: GSM48_MT_RR_CHAN_MODE_MODIF, calling gsm48_rr_rx_chan_modify(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_chan_modify(ms, msg); break; case GSM48_MT_RR_HANDO_CMD: + printf("MTZ: GSM48_MT_RR_HANDO_CMD, calling gsm48_rr_rx_hando_cmd(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_hando_cmd(ms, msg); break; case GSM48_MT_RR_FREQ_REDEF: + printf("MTZ: GSM48_MT_RR_FREQ_REDEF, calling gsm48_rr_rx_frq_redef(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_frq_redef(ms, msg); break; case GSM48_MT_RR_CHAN_REL: + printf("MTZ: GSM48_MT_RR_CHAN_REL, calling gsm48_rr_rx_chan_rel(in gsm48_rr_data_ind)\n"); rc = gsm48_rr_rx_chan_rel(ms, msg); break; case GSM48_MT_RR_APP_INFO: + printf("MTZ: GSM48_MT_RR_APP_INFO, calling none (in gsm48_rr_data_ind)\n"); LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n"); break; default: + printf("MTZ: Unknown, calling none (in gsm48_rr_data_ind)\n"); LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n", gh->msg_type); @@ -4694,18 +5100,25 @@ static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) switch (sih->system_information) { case GSM48_MT_RR_SYSINFO_1: + printf("MTZ: GSM48_MT_RR_SYSINFO_1, calling gsm48_rr_rx_sysinfo1 (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo1(ms, msg); case GSM48_MT_RR_SYSINFO_2: + printf("MTZ: GSM48_MT_RR_SYSINFO_2, calling gsm48_rr_rx_sysinfo2 (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo2(ms, msg); case GSM48_MT_RR_SYSINFO_2bis: + printf("MTZ: GSM48_MT_RR_SYSINFO_2bis, calling gsm48_rr_rx_sysinfo2bis (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo2bis(ms, msg); case GSM48_MT_RR_SYSINFO_2ter: + printf("MTZ: GSM48_MT_RR_SYSINFO_2ter, calling gsm48_rr_rx_sysinfo2ter (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo2ter(ms, msg); case GSM48_MT_RR_SYSINFO_3: + printf("MTZ: GSM48_MT_RR_SYSINFO_3, calling gsm48_rr_rx_sysinfo3 (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo3(ms, msg); case GSM48_MT_RR_SYSINFO_4: + printf("MTZ: GSM48_MT_RR_SYSINFO_4, calling gsm48_rr_rx_sysinfo4 (in gsm48_rr_rx_bcch)\n"); return gsm48_rr_rx_sysinfo4(ms, msg); default: + printf("MTZ: Unsupported, calling none (in gsm48_rr_rx_bcch)\n"); #if 0 LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n", sih->system_information); @@ -4721,19 +5134,26 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) switch (sih->system_information) { case GSM48_MT_RR_PAG_REQ_1: + printf("MTZ: GSM48_MT_RR_PAG_REQ_1, calling gsm48_rr_rx_pag_req_1 (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_pag_req_1(ms, msg); case GSM48_MT_RR_PAG_REQ_2: + printf("MTZ: GSM48_MT_RR_PAG_REQ_2, calling gsm48_rr_rx_pag_req_2 (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_pag_req_2(ms, msg); case GSM48_MT_RR_PAG_REQ_3: + printf("MTZ: GSM48_MT_RR_PAG_REQ_3, calling gsm48_rr_rx_pag_req_3 (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_pag_req_3(ms, msg); case GSM48_MT_RR_IMM_ASS: + printf("MTZ: GSM48_MT_RR_IMM_ASS, calling gsm48_rr_rx_imm_ass (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_imm_ass(ms, msg); case GSM48_MT_RR_IMM_ASS_EXT: + printf("MTZ: GSM48_MT_RR_IMM_ASS_EXT, calling gsm48_rr_rx_imm_ass_ext (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_imm_ass_ext(ms, msg); case GSM48_MT_RR_IMM_ASS_REJ: + printf("MTZ: GSM48_MT_RR_IMM_ASS_REJ, calling gsm48_rr_rx_imm_ass_rej (in gsm48_rr_rx_pch_agch)\n"); return gsm48_rr_rx_imm_ass_rej(ms, msg); default: + printf("MTZ: Unknown, calling none (in gsm48_rr_rx_pch_agch)\n"); #if 0 LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n", sih->system_information); @@ -4742,15 +5162,17 @@ 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; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - struct gsm48_hdr *sih = msgb_l3(msg); + struct gsm48_system_information_type_header *sih = msgb_l3(msg); uint8_t ind_ta, ind_tx_power; + printf("MTZ: In gsm48_rr_rx_acch_unit\n"); + if (msgb_l2len(msg) < sizeof(*rllh) + 2 + 2) { LOGP(DRR, LOGL_ERROR, "Missing TA and TX_POWER IEs\n"); return -EINVAL; @@ -4772,21 +5194,53 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) rr->cd_now.ind_tx_power = ind_tx_power; } - switch (sih->msg_type) { + switch (sih->system_information) { case GSM48_MT_RR_SYSINFO_5: + printf("MTZ: GSM48_MT_RR_SYSINFO_5, calling gsm48_rr_rx_sysinfo5 (in gsm48_rr_rx_acch_unit)\n"); return gsm48_rr_rx_sysinfo5(ms, msg); case GSM48_MT_RR_SYSINFO_5bis: + printf("MTZ: GSM48_MT_RR_SYSINFO_5bis, calling gsm48_rr_rx_sysinfo5bis (in gsm48_rr_rx_acch_unit)\n"); return gsm48_rr_rx_sysinfo5bis(ms, msg); case GSM48_MT_RR_SYSINFO_5ter: + printf("MTZ: GSM48_MT_RR_SYSINFO_5ter, calling gsm48_rr_rx_sysinfo5ter (in gsm48_rr_rx_acch_unit)\n"); return gsm48_rr_rx_sysinfo5ter(ms, msg); case GSM48_MT_RR_SYSINFO_6: + printf("MTZ: GSM48_MT_RR_SYSINFO_6, calling gsm48_rr_rx_sysinfo6 (in gsm48_rr_rx_acch_unit)\n"); return gsm48_rr_rx_sysinfo6(ms, msg); default: + printf("MTZ: Unknown, calling none (in gsm48_rr_rx_acch_unit)\n"); LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", - sih->msg_type); + sih->system_information); return -EINVAL; } } + +/* 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) @@ -4816,8 +5270,10 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) cs->ccch_state = GSM322_CCCH_ST_DATA; /* in dedicated mode */ - if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND){ + printf("MTZ: gsm48_rr_tx_rand_acc called 2 (in gsm48_rr_unit_data_ind)\n"); return gsm48_rr_tx_rand_acc(ms, NULL); + } /* set timer for reading BCCH */ if (cs->state == GSM322_C2_STORED_CELL_SEL @@ -4836,15 +5292,27 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); switch (ch_type) { case RSL_CHAN_PCH_AGCH: + printf("MTZ: RSL_CHAN_PCH_AGCH, calling gsm48_rr_rx_pch_agch (after gsm48_rcv_rll)\n"); return gsm48_rr_rx_pch_agch(ms, msg); case RSL_CHAN_BCCH: + printf("MTZ: RSL_CHAN_BCCH, calling gsm48_rr_rx_bcch (after gsm48_rcv_rll)\n"); return gsm48_rr_rx_bcch(ms, msg); case RSL_CHAN_Bm_ACCHs: + printf("MTZ: RSL_CHAN_Bm_ACCHs, calling none (after gsm48_rcv_rll)\n"); case RSL_CHAN_Lm_ACCHs: + printf("MTZ: RSL_CHAN_Lm_ACCHs, calling none (after gsm48_rcv_rll)\n"); case RSL_CHAN_SDCCH4_ACCH: + printf("MTZ: RSL_CHAN_SDCCH4_ACCH, calling none (after gsm48_rcv_rll)\n"); case RSL_CHAN_SDCCH8_ACCH: - return gsm48_rr_rx_acch(ms, msg); + if ((rllh->link_id & 0x40)){ + printf("MTZ: RSL_CHAN_SDCCH8_ACCH, calling gsm48_rr_rx_acch_unit (after gsm48_rcv_rll)\n"); + return gsm48_rr_rx_acch_unit(ms, msg); + } else { + printf("MTZ: RSL_CHAN_SDCCH8_ACCH, calling gsm48_rr_rx_dcch_unit (after gsm48_rcv_rll)\n"); + return gsm48_rr_rx_dcch_unit(ms, msg); + } default: + printf("MTZ: unknown, calling none (after gsm48_rcv_rll)\n"); LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", rllh->chan_nr); return -EINVAL; @@ -4890,6 +5358,38 @@ 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); + printf("MTZ: gsm48_rr_activate_channel called 5\n"); + 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) @@ -4898,42 +5398,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; @@ -4970,6 +5440,8 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) uint8_t cause = rllh->data[2]; uint8_t link_id = rllh->link_id; + printf("MTZ: In gsm48_rr_mdl_error_ind\n"); + switch (cause) { case RLL_CAUSE_SEQ_ERR: case RLL_CAUSE_UNSOL_DM_RESP_MF: @@ -5025,6 +5497,8 @@ static int gsm48_rr_estab_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; + printf("MTZ: In gsm48_rr_estab_ind_sapi3\n"); + if (rr->state != GSM48_RR_ST_DEDICATED) { /* disconnect sapi 3 link */ gsm48_release_sapi3_link(ms); @@ -5061,6 +5535,8 @@ static int gsm48_rr_estab_cnf_sapi3(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; + printf("MTZ: In gsm48_rr_estab_cnf_sapi3\n"); + if (rr->state != GSM48_RR_ST_DEDICATED) { gsm48_release_sapi3_link(ms); return -EINVAL; @@ -5090,6 +5566,8 @@ static int gsm48_rr_data_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_rr_hdr *rrh; uint8_t pdisc = gh->proto_discr & 0x0f; + printf("MTZ: In gsm48_rr_data_ind_sapi3\n"); + if (pdisc == GSM48_PDISC_RR) { msgb_free(msg); return -EINVAL; @@ -5115,6 +5593,8 @@ static int gsm48_rr_rel_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; + printf("MTZ: In gsm48_rr_rel_ind_sapi3\n"); + new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE); LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is released\n"); @@ -5251,11 +5731,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}, }; @@ -5302,9 +5777,15 @@ static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg) "%s (link_id 0x%x)\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state], link_id); + //MTZ - added below + LOGP(DCS, LOGL_INFO, "In gsm48_rcv_rll - (ms %s) Received '%s' from L2 in state " + "%s (link_id 0x%x)\n", ms->name, rsl_msg_name(msg_type), + gsm48_rr_state_names[rr->state], link_id); + /* find function for current state and message */ if (!(link_id & 7)) { /* SAPI 0 */ + printf("MTZ - SAP 0\n"); for (i = 0; i < DLDATASLLEN; i++) if ((msg_type == dldatastatelist[i].type) && ((1 << rr->state) & dldatastatelist[i].states)) @@ -5319,6 +5800,7 @@ static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg) rc = dldatastatelist[i].rout(ms, msg); } else { /* SAPI 3 */ + printf("MTZ - SAP 3\n"); for (i = 0; i < DLDATASLLENS3; i++) if ((msg_type == dldatastatelists3[i].type) && ((1 << rr->sapi3_state) & @@ -5354,10 +5836,20 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) if (rr->state == GSM48_RR_ST_CONN_PEND && msg_type == RSL_MT_CHAN_CONF) { + printf("MTZ: RSL_MT_CHAN_CONF with GSM48_RR_ST_CONN_PEND, calling gsm48_rr_tx_rand_acc (after gsm48_rcv_cch)\n"); + printf("MTZ: gsm48_rr_tx_rand_acc called 3 (in gsm48_rcv_cch)\n"); rc = gsm48_rr_tx_rand_acc(ms, msg); msgb_free(msg); return rc; } + + if (rr->state == GSM48_RR_ST_DEDICATED + && msg_type == RSL_MT_CHAN_CONF) { + printf("MTZ: RSL_MT_CHAN_CONF with GSM48_RR_ST_DEDICATED, calling gsm48_rr_rand_acc_cnf_dedicated (after gsm48_rcv_cch)\n"); + 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); @@ -5373,13 +5865,16 @@ static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg) switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: + printf("MTZ: In gsm48_rcv_rsl, calling gsm48_rcv_rll\n"); rc = gsm48_rcv_rll(ms, msg); break; case ABIS_RSL_MDISC_COM_CHAN: + printf("MTZ: In gsm48_rcv_rsl, calling gsm48_rcv_cch\n"); rc = gsm48_rcv_cch(ms, msg); break; default: /* FIXME: implement this */ + printf("MTZ: In gsm48_rcv_rsl, unknown message\n"); LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", rslh->msg_discr); msgb_free(msg); @@ -5536,95 +6031,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 -} - -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; - osmo_timer_schedule(&rr->t3124, sec, micro); -} - -/* 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; diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile index 914db6c7..24fbea28 100644 --- a/src/target/firmware/Makefile +++ b/src/target/firmware/Makefile @@ -136,7 +136,7 @@ INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include -I../. # # Uncomment this line if you want to enable Tx (Transmit) Support. -#CFLAGS += -DCONFIG_TX_ENABLE +CFLAGS += -DCONFIG_TX_ENABLE # Uncomment this line if you want to write to flash. #CFLAGS += -DCONFIG_FLASH_WRITE diff --git a/src/target/firmware/Makefile.mtk b/src/target/firmware/Makefile.mtk index 927e31a0..30fa2fcf 100644 --- a/src/target/firmware/Makefile.mtk +++ b/src/target/firmware/Makefile.mtk @@ -4,16 +4,14 @@ BOARDS?=mt62xx # List of all applications (meant to be overridden on command line) APPLICATIONS?=loader_mtk -APP_loader_mtk_ENVIRONMENTS=mtkram - -ENV_mtkram_LDS=board/mediatek/ram.lds -ENV_mtkram_OBJS=board/mediatek/start.ram.o +mtkram_LDS=board/mediatek/ram.lds +mtkram_OBJS=board/mediatek/start.ram.o mtk_COMMON_OBJS=board/mediatek/uart.o # Mediatek MT62xx -BOARD_mt62xx_OBJS=$(mtk_COMMON_OBJS) board/mt62xx/init.o -BOARD_mt62xx_ENVIRONMENTS=mtkram +mt62xx_OBJS=$(mtk_COMMON_OBJS) board/mt62xx/init.o +mt62xx_ENVIRONMENTS=mtkram # Global include path INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h index de996a67..b5cc2516 100644 --- a/src/target/firmware/include/layer1/async.h +++ b/src/target/firmware/include/layer1/async.h @@ -33,7 +33,7 @@ int l1a_txq_msgb_count(struct llist_head *queue); void l1a_txq_msgb_flush(struct llist_head *queue); /* request a RACH */ -void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra); +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint16_t arfcn); /* schedule frequency change */ void l1a_freq_req(uint32_t fn_sched); diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h index 74e2d271..3c9aa8cb 100644 --- a/src/target/firmware/include/layer1/mframe_sched.h +++ b/src/target/firmware/include/layer1/mframe_sched.h @@ -4,6 +4,8 @@ #include enum mframe_task { + MF_TASK_TEST1, + MF_TASK_BCCH_NORM, MF_TASK_BCCH_EXT, MF_TASK_CCCH, diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h index 30c51ae6..5c2cb963 100644 --- a/src/target/firmware/include/layer1/prim.h +++ b/src/target/firmware/include/layer1/prim.h @@ -20,7 +20,7 @@ void l1s_nb_test(uint8_t base_fn); void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req); void l1a_freq_req(uint32_t fn_sched); -void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra); +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint16_t arfcn); /* Primitives raw scheduling sets */ extern const struct tdma_sched_item nb_sched_set[]; @@ -29,6 +29,10 @@ extern const struct tdma_sched_item nb_sched_set_ul[]; extern const struct tdma_sched_item tch_sched_set[]; extern const struct tdma_sched_item tch_a_sched_set[]; extern const struct tdma_sched_item tch_d_sched_set[]; -extern const struct tdma_sched_item neigh_pm_sched_set[]; +extern const struct tdma_sched_item neigh_pm_idle_sched_set[]; +extern const struct tdma_sched_item neigh_pm_tch_sched_set[]; +extern const struct tdma_sched_item neigh_sync_sched_set[]; + +extern const struct tdma_sched_item test1_sched[]; #endif /* _L1_PRIM_H */ diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index 3565ee20..df3c6fdc 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -55,6 +55,17 @@ struct l1s_h1 { }; struct l1s_state { + + //MTZ + int new_dm; + uint32_t orig_tpu_offset; + uint32_t tpu_offsets[64]; + uint32_t nb_freq_diff[64]; + uint32_t nb_frame_diff[64]; + int32_t nb_sb_freq_diff[64]; + uint32_t nb_sb_snr[64]; + uint16_t tpu_offsets_arfcn[64]; + struct gsm_time current_time; /* current GSM time */ struct gsm_time next_time; /* GSM time at next TMDMA irq */ @@ -127,6 +138,7 @@ struct l1s_state { GSM_DCHAN_UNKNOWN, } type; + uint8_t chan_nr; uint8_t scn; uint8_t tsc; uint8_t tn; @@ -145,19 +157,37 @@ struct l1s_state { struct l1s_h0 st_h0; struct l1s_h1 st_h1; }; + + uint8_t rx_only; } dedicated; /* neighbour cell power measurement process */ struct { - uint8_t n, second; - uint8_t pos; - uint8_t running; - uint16_t band_arfcn[64]; - uint8_t tn[64]; - uint8_t level[64]; + uint32_t start_fn; /* frame number of measumrement start */ + uint8_t valid; /* we have a complete set of measurements */ + uint8_t rounds; /* current rounds of complete measurements */ + uint8_t pos; /* current neighbor to measure */ + uint8_t running; /* DSP task running */ + uint8_t n; /* number of neighbors to measure */ + uint16_t band_arfcn[64]; /* list of ARFCNs */ + uint8_t tn[64]; /* list of TS offset for each measurement */ + uint16_t level_sum[64]; /* sum while processing rounds */ + uint8_t level[64]; /* latest results */ + uint32_t tpu_offset[64]; } neigh_pm; + + /* neighbor cell SCH sync process */ + struct { + uint8_t flags_bsic[64]; /* flags + bsic */ + uint8_t count; /* counter for sync process */ + uint8_t index; /* cell of current sync process (0..63) */ + uint8_t running; /* DSP task running */ + } neigh_sb; }; +#define NEIGH_PM_FLAG_SCANNED 0x80 +#define NEIGH_PM_FLAG_BSIC 0x40 + extern struct l1s_state l1s; struct l1s_meas_hdr { diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h index 7b146f12..287d8962 100644 --- a/src/target/firmware/include/layer1/tpu_window.h +++ b/src/target/firmware/include/layer1/tpu_window.h @@ -6,6 +6,8 @@ enum l1_rxwin_type { L1_RXWIN_FB, /* FCCH burst detection */ L1_RXWIN_SB, /* SCH burst detection */ L1_RXWIN_NB, /* Normal burst decoding */ + L1_RXWIN_FB26, /* SCH burst detection of neighbour cell */ + L1_RXWIN_SB26, /* SCH burst detection of neighbour cell */ _NUM_L1_RXWIN }; diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c index a51a1071..bf47a6e4 100644 --- a/src/target/firmware/layer1/afc.c +++ b/src/target/firmware/layer1/afc.c @@ -93,7 +93,10 @@ void afc_correct(int16_t freq_error, uint16_t arfcn) } delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); - printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + //MTZ + //printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + // freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + printf("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); afc_state.dac_value += delta; diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index daffaf8b..28c900a6 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -236,6 +236,10 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload; + //MTZ - added + l1s.new_dm = 1; + //l1s.ho_arfcn = 0; + printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n", ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc); @@ -243,10 +247,11 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) mframe_disable(MF_TASK_NEIGH_PM51_C0T0); /* configure dedicated channel state */ - l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); - l1s.dedicated.tsc = est_req->tsc; - l1s.dedicated.tn = ul->chan_nr & 0x7; - l1s.dedicated.h = est_req->h; + l1s.dedicated.chan_nr = ul->chan_nr; + l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); + l1s.dedicated.tsc = est_req->tsc; + l1s.dedicated.tn = ul->chan_nr & 0x7; + l1s.dedicated.h = est_req->h; if (est_req->h) { int i; @@ -271,8 +276,15 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) /* Audio path */ audio_set_enabled(est_req->tch_mode, est_req->audio_mode); } + + /* Handover config */ + if ((est_req->flags & L1CTL_EST_F_RXONLY)) + l1s.dedicated.rx_only = 1; + else + l1s.dedicated.rx_only = 0; /* figure out which MF tasks to enable */ + l1s.neigh_pm.n = 0; //MTZ - Uncomment l1a_mftask_set(chan_nr2mf_task_mask(ul->chan_nr, NEIGH_MODE_PM)); } @@ -337,7 +349,7 @@ static void l1ctl_rx_dm_rel_req(struct msgb *msg) dsp_load_ciph_param(0, NULL); l1a_tch_mode_set(GSM48_CMODE_SIGN); audio_set_enabled(GSM48_CMODE_SIGN, 0); - l1s.neigh_pm.n = 0; + l1s.neigh_pm.n = 0; //MTZ - Uncomment } /* receive a L1CTL_PARAM_REQ from L23 */ @@ -352,6 +364,7 @@ static void l1ctl_rx_param_req(struct msgb *msg) l1s.ta = par_req->ta; l1s.tx_power = par_req->tx_power; + l1s.dedicated.rx_only = 0; } /* receive a L1CTL_RACH_REQ from L23 */ @@ -361,11 +374,12 @@ static void l1ctl_rx_rach_req(struct msgb *msg) struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload; - printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n", - rach_req->ra, ntohs(rach_req->offset), rach_req->combined); + //printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n", + printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d arfcn=%d)\n", //MTZ + rach_req->ra, ntohs(rach_req->offset), rach_req->combined, rach_req->arfcn); l1a_rach_req(ntohs(rach_req->offset), rach_req->combined, - rach_req->ra); + rach_req->ra, rach_req->arfcn); //MTZ - last added } /* receive a L1CTL_DATA_REQ from L23 */ @@ -376,14 +390,16 @@ static void l1ctl_rx_data_req(struct msgb *msg) struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *) ul->payload; struct llist_head *tx_queue; - printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id); + //MTZ - commenting as causing issues in firmware + //printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id); msg->l3h = data_ind->data; if (ul->link_id & 0x40) { struct gsm48_hdr *gh = (struct gsm48_hdr *)(data_ind->data + 5); if (gh->proto_discr == GSM48_PDISC_RR && gh->msg_type == GSM48_MT_RR_MEAS_REP) { - printd("updating measurement report\n"); + //MTZ - commenting as causing issues in firmware + //printd("updating measurement report\n"); l1a_meas_msgb_set(msg); return; } @@ -445,6 +461,7 @@ static void l1ctl_rx_reset_req(struct msgb *msg) l1s_reset(); l1s_reset_hw(); audio_set_enabled(GSM48_CMODE_SIGN, 0); + l1s.dedicated.type = GSM_DCHAN_NONE; l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type); break; case L1CTL_RES_T_SCHED: @@ -510,6 +527,15 @@ static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode) l1_queue_for_l2(msg); } +//MTZ +/* Transmit a L1CTL_TCH_MODE_CONF */ +static void l1ctl_test(void) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TEST); + + l1_queue_for_l2(msg); +} + /* receive a L1CTL_TCH_MODE_REQ from L23 */ static void l1ctl_rx_tch_mode_req(struct msgb *msg) { @@ -541,20 +567,32 @@ static void l1ctl_rx_neigh_pm_req(struct msgb *msg) /* reset list in order to prevent race condition */ l1s.neigh_pm.n = 0; /* atomic */ - l1s.neigh_pm.second = 0; /* now reset pointer and fill list */ l1s.neigh_pm.pos = 0; + l1s.neigh_pm.valid = 0; + l1s.neigh_pm.rounds = 0; l1s.neigh_pm.running = 0; + if (pm_req->n > 64) + pm_req->n = 64; for (i = 0; i < pm_req->n; i++) { l1s.neigh_pm.band_arfcn[i] = ntohs(pm_req->band_arfcn[i]); l1s.neigh_pm.tn[i] = pm_req->tn[i]; + l1s.neigh_pm.level[i] = 0; + l1s.neigh_sb.flags_bsic[i] = 0; } + l1s.neigh_sb.count = 0; printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req->n); l1s.neigh_pm.n = pm_req->n; /* atomic */ - /* on C0 enable PM on frame 51 */ + /* + * IDLE: on C0 enable PM on frame 51 + * DEDICATED: add neighbor cell task + */ if (l1s.dedicated.type == GSM_DCHAN_NONE) mframe_enable(MF_TASK_NEIGH_PM51_C0T0); + else + l1a_mftask_set(chan_nr2mf_task_mask(l1s.dedicated.chan_nr, + NEIGH_MODE_PM)); } /* receive a L1CTL_TRAFFIC_REQ from L23 */ diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 7fa38c13..b4109821 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -50,7 +50,9 @@ struct mframe_sched_item { #define NB_QUAD_FH_DL NB_QUAD_DL #define NB_QUAD_UL nb_sched_set_ul #define NB_QUAD_FH_UL NB_QUAD_UL -#define NEIGH_PM neigh_pm_sched_set +#define NEIGH_PM_IDLE neigh_pm_idle_sched_set +#define NEIGH_PM_TCH neigh_pm_tch_sched_set +#define NEIGH_SYNC neigh_sync_sched_set /* BCCH Normal */ static const struct mframe_sched_item mf_bcch_norm[] = { @@ -210,17 +212,17 @@ static const struct mframe_sched_item mf_sdcch4_cbch[] = { /* Measurement for MF 51 C0 */ static const struct mframe_sched_item mf_neigh_pm51_c0t0[] = { - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 0 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 10 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 20 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 30 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 40 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 10 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 20 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 30 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 40 }, { .sched_set = NULL } }; /* Measurement for MF 51 */ static const struct mframe_sched_item mf_neigh_pm51[] = { - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 50 }, + { .sched_set = NEIGH_PM_IDLE , .modulo = 51, .frame_nr = 50 }, { .sched_set = NULL } }; @@ -275,16 +277,20 @@ static const struct mframe_sched_item mf_tch_h_0[] = { { .sched_set = TCH, .modulo = 13, .frame_nr = 6 }, { .sched_set = TCH_D, .modulo = 13, .frame_nr = 7 }, { .sched_set = TCH, .modulo = 13, .frame_nr = 8 }, - { .sched_set = TCH_D, .modulo = 13, .frame_nr = 9 }, - { .sched_set = TCH, .modulo = 13, .frame_nr = 10 }, - { .sched_set = TCH_D, .modulo = 13, .frame_nr = 11 }, + //{ .sched_set = TCH_D, .modulo = 13, .frame_nr = 9 }, + { .sched_set = TCH_D, .modulo = 26, .frame_nr = 9 }, + //{ .sched_set = TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH, .modulo = 26, .frame_nr = 10 }, + //{ .sched_set = TCH_D, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH_D, .modulo = 26, .frame_nr = 11 }, { .sched_set = TCH_A, .modulo = 26, .frame_nr = 12, .flags = MF_F_SACCH }, { .sched_set = NULL } }; static const struct mframe_sched_item mf_tch_h_1[] = { - { .sched_set = TCH_D, .modulo = 13, .frame_nr = 0 }, + //{ .sched_set = TCH_D, .modulo = 13, .frame_nr = 0 }, + { .sched_set = TCH_D, .modulo = 26, .frame_nr = 0 }, { .sched_set = TCH, .modulo = 13, .frame_nr = 1 }, { .sched_set = TCH_D, .modulo = 13, .frame_nr = 2 }, { .sched_set = TCH, .modulo = 13, .frame_nr = 3 }, @@ -294,20 +300,44 @@ static const struct mframe_sched_item mf_tch_h_1[] = { { .sched_set = TCH, .modulo = 13, .frame_nr = 7 }, { .sched_set = TCH_D, .modulo = 13, .frame_nr = 8 }, { .sched_set = TCH, .modulo = 13, .frame_nr = 9 }, - { .sched_set = TCH_D, .modulo = 13, .frame_nr = 10 }, - { .sched_set = TCH, .modulo = 13, .frame_nr = 11 }, + //{ .sched_set = TCH_D, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH_D, .modulo = 26, .frame_nr = 23 }, + //{ .sched_set = TCH, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH, .modulo = 26, .frame_nr = 24 }, { .sched_set = TCH_A, .modulo = 26, .frame_nr = 25, .flags = MF_F_SACCH }, { .sched_set = NULL } }; -/* Measurement for MF 26 */ +/* + * Measurement for MF 26 + * Note: PM will be read 2 frames, later, so we can only measure every second + * frame. + */ static const struct mframe_sched_item mf_neigh_pm26_even[] = { - { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 25 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 6 }, + //{ .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 26, .frame_nr = 8 }, + //{ .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 26, .frame_nr = 10 }, + //{ .sched_set = NEIGH_SYNC, .modulo = 26, .frame_nr = 24 }, + { .sched_set = NEIGH_SYNC, .modulo = 26, .frame_nr = 22 }, { .sched_set = NULL } }; static const struct mframe_sched_item mf_neigh_pm26_odd[] = { - { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 12 }, + //{ .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 26, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 8 }, + //{ .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 26, .frame_nr = 23 }, + //{ .sched_set = NEIGH_SYNC, .modulo = 26, .frame_nr = 11 }, + { .sched_set = NEIGH_SYNC, .modulo = 26, .frame_nr = 10 }, { .sched_set = NULL } }; @@ -318,6 +348,7 @@ static const struct mframe_sched_item mf_tx_all_nb[] = { }; static const struct mframe_sched_item *sched_set_for_task[32] = { + [MF_TASK_BCCH_NORM] = mf_bcch_norm, [MF_TASK_BCCH_EXT] = mf_bcch_ext, [MF_TASK_CCCH] = mf_ccch, diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c index 50acefcc..dafd06f0 100644 --- a/src/target/firmware/layer1/prim_fbsb.c +++ b/src/target/firmware/layer1/prim_fbsb.c @@ -84,6 +84,16 @@ struct l1a_fb_state { static struct l1a_fb_state fbs; static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +static int fb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added +int total_sb_det = 0; + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle0 = 0; //MTZ - This was added +uint16_t nb_fb_angle1 = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added static void dump_mon_state(struct mon_state *fb) { @@ -141,6 +151,7 @@ static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) /* TS 05.02 Chapter 4.3.3 TDMA frame number */ time->fn = gsm_gsmtime2fn(time); + printf("\n\nMTZ: time->fn = %d\n\n", time->fn); time->tc = (time->fn / 51) % 8; @@ -168,6 +179,30 @@ static void read_sb_result(struct mon_state *st, int attempt) dsp_api.r_page_used = 1; } +static void read_sb_result2(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + printf("\n\n\nMTZ: st->freq_diff = %d\n\n\n", st->freq_diff); + + //MTZ - commenting out for now + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + /* Note: When we get the SB response, it is 2 TDMA frames after the SB * actually happened, as it is a "C W W R" task */ #define SB2_LATENCY 2 @@ -235,6 +270,7 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, synchronize_tdma(&l1s.serving_cell); /* if we have recived a SYNC burst, update our local GSM time */ + printf("\n\nMTZ: current_fn = %d, fn from SB = %d\n\n", gsm_gsmtime2fn(&l1s.current_time), fbs.mon.time.fn + SB2_LATENCY); gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); /* compute next time from new current time */ l1s.next_time = l1s.current_time; @@ -258,6 +294,7 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, l1s_reset_hw(); /* enable the MF Task for BCCH reading */ mframe_enable(MF_TASK_BCCH_NORM); + printf("\nMTZ: l1s.serving_cell.ccch_mode = %d\n", l1s.serving_cell.ccch_mode); if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) mframe_enable(MF_TASK_CCCH_COMB); else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) @@ -269,6 +306,9 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, l1s_compl_sched(L1_COMPL_FB); + //MTZ - delete this + //mframe_enable(MF_TASK_TEST1); + return 0; } @@ -284,11 +324,14 @@ static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, dsp_api.ndb->d_fb_mode = 0; /* wideband search */ /* Program TPU */ + printf("\nMTZ: arfcn in l1s_sbdet_cmd = %d\n", rf_arfcn); l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); return 0; } +static const struct tdma_sched_item sb_sched_set[]; + /* This is how it is done by the TSM30 */ static const struct tdma_sched_item sb_sched_set[] = { SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), @@ -384,6 +427,7 @@ static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, dsp_api.ndb->d_fb_mode = fb_mode; /* Program TPU */ + printf("\nMTZ: arfcn in l1s_fbdet_cmd = %d\n", fbs.req.band_arfcn); l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); return 0; @@ -562,15 +606,601 @@ void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) /* Reset the TOA loop counters */ toa_reset(); - if (fbs.req.flags & L1CTL_FBSB_F_FB0) - tdma_schedule_set(base_fn, fb_sched_set, 0); - else if (fbs.req.flags & L1CTL_FBSB_F_FB1) - tdma_schedule_set(base_fn, fb_sched_set, 0); - else if (fbs.req.flags & L1CTL_FBSB_F_SB) - tdma_schedule_set(base_fn, sb_sched_set, 0); + tdma_schedule_set(0, fb_sched_set, 0); + + //MTZ - Changed + //if (fbs.req.flags & L1CTL_FBSB_F_FB0) + // tdma_schedule_set(base_fn, fb_sched_set, 0); + //else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + // tdma_schedule_set(base_fn, fb_sched_set, 0); + //else if (fbs.req.flags & L1CTL_FBSB_F_SB) + // tdma_schedule_set(base_fn, sb_sched_set, 0); + + //MTZ + //l1ctl_test(); + //struct msgb *msg1 = l1ctl_msgb_alloc(L1CTL_TEST); + + //l1_queue_for_l2(msg1); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/*////////////////////////////////////////////////////////////////////////////////////////////////////// + +FB/SB detection in dedicated mode + +- First of all it must be noted that 3 additional frames have been added to the original idle frame 25 or 12 depending upon the half rate type. This gives us additional time to do any required time and frequency synchronization before detecting the synchronization burst of the neighbour as well as returning to the current settings before the TCH frame that follows. This can be seen in the file firmware/layer1/mframe_sched.c. + +- The approach to detect FB and SB in dedicated mode must be kept in mind. In dedicated mode the traffic multiframe is running on the MS that consists of 26 frames with frame 25 as idle frame (in even mode). In order to read FB of neighbour we need to read its control channel that consists of 51 frames. As the control MF size is not a multiple of traffic MF size each time the traffic idle frame appears we are at a different frame number in the control MF of the neighbour. Hence the traffic idle frame coincides with a different control channel each time and actually traverses through it. By looking at the channel assignment accross the control MF it can be seen that we will coincide with a frequency burst on the control channel when we have an idle traffic frame every 10 or 11 idle frames. If we repeatedly search for FB on traffic idle frames we should be able to detect it every 10 or 11 idle frames. The SB would appear the second idle frame following the idle frame on which FB is detected not the next (this can be verified by hand). This is the approach we use. + +- It was seen in idle mode FB/SB detection that FB is detected twice, first using FB mode 0 then using FB mode 1 which seem to provide different precision on frequency correction. After each FB detection frequency correction is performed in idle mode using afc_correct. Following FB mode 1 FB detection the quarter-bit synchronization is also performed using TOA (time of arrival of frequency burst) to synch the start of frame with the internal counters by modifying l1s.tpu_offset from what I could find. It is only after these two kinds of synchronizations/corrections that we can read the bursts on the channel properly. Synchronization burst is then read which is used to get the BSIC of the BTS and the absolute frame number it is on to update our internal variables/registers with it. Additionally further frequency correction is performed. + +- The same steps need to be performed as above but this time in steps as we can only do this whenever the idle frame appears otherwise we would be in traffic mode and wouldn't have time to perform this. At each step values for frequency compensation/time correction are stored to be used in the next step. At the beginning of every idle frame set (as we have added three more idle frames) the required synchronization/correction is performed and at the end of it it is reversed to return to original settings. l1s_neigh_fbsb_sync is used to perform any synching/correction at the start of the idle frame set. + +- fb_det and sb_det are used as control variables to guide through the synchronization process as follows: + + fb_det sb_det task + + 0 0 FB detection mode 0 + 1 0 FB detection mode 1 + 0 1 Do nothing - this is the idle frame following FB detection + 0 2 SB detection + 0 3 Do nothing + 0 4 SB detection again + +fb_det goes from 0 to 1 upon FB detection in FB mode 0. Then when FB is detected in FB mode 1 sb_det becomes 1 and fb_det goes to 0. Thereafter sb_det increments upon every idle frame. This is important to keep in mind. + +* SB detection is done second time to make sure the SB isn't missed as I thought it might be in the middle of the frames. Just a cautious check. + +- Whenever FB and/or SB are detected the results are stored in the following variables to be used in case of handover + + + l1s.tpu_offsets_arfcn[ii] - The corresponding arfcn for the indices + l1s.tpu_offsets[ii] - The quarter-bit offset required for start of frame + l1s.nb_freq_diff[ii] - Frequency correction required from FB mode 0 + 1 + l1s.nb_sb_freq_diff[ii] - Additional freq correction from SB detection + l1s.nb_frame_diff[ii] - The difference in frame of serving cell and the neighbour + l1s.nb_sb_snr[ii] - The snr received (might not really be needed) + +I know I could have used the l1s.neigh_sb struct but for now that's the way it is. This is so also because the stored number of neighbour gets reset whenever we enter a new dedicated mode but that can be worked around. + +- The TOA obtained from FB detection gives the number of GSM bits from the start of the command to detect FB till the actual detection. As it can span more than 1 frame especially in idle mode it is broken down to ntdma and qbits. ntdma denotes the complete frames and qbits the number of quarter-bits following an integer number of frames (the remainder bits x 4). This is the actual difference from the frame start of the serving cell to the frame start of the neighbour cell. + +- This schedule is activated using the multiframe scheduler whenever the idle frame comes in dedicated mode. select_neigh_cell() is used to select the next neighbour once SB of one neighbour is obtained or 15 tries to detect FB have failed (or SB is not detected following FB detection). + +- l1s.neigh_sb is the struct used to keep track of neigbour bsic/power measurements etc. This can be enhanced to store the values menioned above as well. + +- l1s.tpu_offset is the variable storing the quarter-bit offset to the start of frame for the cell to be read/transmitted to. afc_correct(...) is used to do frequency compensation. + +- Things could have been coded in a better way but that's the way it is for now. Perhaps someone else can restructure the whole thing and come up with a better approach. + +///////////////////////////////////////////////////////////////////////////////////////////////////// */ + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_sync(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + if (fb_det == 1) { + //printf("afc_correct in l1s_neigh_fbsb_sync for FB1 - nb_fb_angle0\n\n"); + afc_correct(ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + } + + if ((sb_det == 2)||(sb_det == 4)) { + //printf("\nMTZ - in l1s_neigh_fbsb_sync, old_tpu_offset = %d\n", l1s.tpu_offset); + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; //MTZ - uncomment + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; //MTZ - uncomment + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + //printf("MTZ - old_tpu_offset = %d, tpu_shift = %d, qbits = %d\n", old_tpu_offset, tpu_shift, qbits); + + l1s.neigh_pm.tpu_offset[l1s.neigh_sb.index] = tpu_shift; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle0)+ANGLE_TO_FREQ(nb_fb_angle1); + break; + } + if (l1s.tpu_offsets_arfcn[ii] == 0) { + l1s.tpu_offsets_arfcn[ii] = l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]; + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle0)+ANGLE_TO_FREQ(nb_fb_angle1); + break; + } + } + + //printf("\n\nMTZ: Stored TPU Offsets, Angles:"); + //for (ii=0; ii<64; ii++) { + // if (l1s.tpu_offsets_arfcn[ii] == 0) + // break; + // printf(" %d,%d(%d)", l1s.tpu_offsets[ii], l1s.nb_freq_diff[ii], l1s.tpu_offsets_arfcn[ii]); + //} + //printf("\n\n"); + + //MTZ - possibly remove the >=50 if statement + if (nb_fb_toa >= 50) { + l1s.tpu_offset = tpu_shift; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(tpu_shift); + } + //printf("afc_correct in l1s_neigh_fbsb_sync for SB - nb_fb_angle0+nb_fb_angle1\n\n"); + afc_correct(ANGLE_TO_FREQ(nb_fb_angle0)+ANGLE_TO_FREQ(nb_fb_angle1), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + } + +} +/* scheduler callback to issue a FB and SB detection task to the DSP in dedicated mode */ +// READ COMMENTS ABOVE l1s_neigh_fbsb_sync +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + +// //MTZ - putting this for now as we wanted to repeatedly detect the remaining ones - remove +// while (!((l1s.neigh_sb.flags_bsic[index] & NEIGH_PM_FLAG_BSIC) == 0)) { +//// printf("\nMTZ: BSIC has been decoded for ARFCN %d (flags_bsic[%d] = %d)\n\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.flags_bsic[index]); +// l1s.neigh_sb.count = 0; +// l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; +// select_neigh_cell(); +// index = l1s.neigh_sb.index; +// } + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + + //printf(" - detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + if (fb_det == 1) { + dsp_api.ndb->d_fb_mode = 1; + } else { + dsp_api.ndb->d_fb_mode = 0; + } + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works - as we have multiple idle frames now we can use TS 0 + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if ((sb_det == 2)||(sb_det == 4)) { + +// printf(" - detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + //MTZ Changed + //dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.db_w->d_task_md = SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works - as we have multiple idle frames now we can use TS 0 + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB and SB detection task to the DSP in dedicate mode */ +// READ COMMENTS ABOVE l1s_neigh_fbsb_sync +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + uint8_t bsic; + int sb_found = 0; + + if (sb_det == 0) { + if (fb_det == 1) { + //printf("afc_correct (-ve) in l1s_neigh_fbsb_resp for FB1 - nb_fb_angle0\n\n"); + afc_correct(-1*ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + } + if (!dsp_api.ndb->d_fb_det) { + printf("MTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 0\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det); + + /* next sync */ + if (++l1s.neigh_sb.count == 15) { + //MTZ - a count of 0 will result in select_neigh_cell() being called and next cell being selected + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + //MTZ - If 15 tries in FB mode 1 then set fb_det to 0 + if (fb_det == 1){ + fb_det = 0; + } + } + + } else { + //MTZ - Capturing the readings from FB detection - these are stored in arrays upon SB detection for use in case handover is required + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + if (fb_det == 1) + nb_fb_angle1 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + else + nb_fb_angle0 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\n\nMTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 1 >>> nb_fb_toa = %d, angle = %d\n\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det, nb_fb_toa, dsp_api.ndb->a_sync_demod[D_ANGLE]); + //MTZ - If FB mode was 0 make it 1, it FB mode was one set sb_det to 1 to indicate SB detection step + if (fb_det == 0) { + fb_det = 1; + l1s.neigh_sb.count = 1; + } else { + sb_det = 1; + fb_det = 0; + } + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + if ((sb_det == 2)||(sb_det == 4)) { + /* check if sync was successful */ + + //MTZ - This was the main change below - we need to read a_sch26 as opposed to a_sch + //if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + bsic = (sb >> 2) & 0x3f; + + t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + fn = (51 * ((t3 - t2 + 26) % 26) + t3 + (26 * 51 * t1)) + SB2_LATENCY; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.nb_frame_diff[ii] = fn - l1s.current_time.fn; + l1s.nb_sb_freq_diff[ii] = ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + l1s.nb_sb_snr[ii] = dsp_api.db_r->a_serv_demod[D_SNR]; + break; + } + } + + total_sb_det++; + //printf("=> SB 0x%08"PRIx32": BSIC=%u \n\n", sb, bsic); + printf("\n----------------------------------------------------------------------------\nSB found = 1 (ARFCN %d, power %d dbm) => SB 0x%08"PRIx32": BSIC=%u, TOA=%d, Angle=%d (Total=%d)\n----------------------------------------------------------------------------\n\n", l1s.neigh_pm.band_arfcn[index], rxlev2dbm(l1s.neigh_pm.level[index]), sb, bsic, dsp_api.db_r->a_serv_demod[D_TOA], dsp_api.db_r->a_serv_demod[D_ANGLE], total_sb_det); + l1s.neigh_sb.flags_bsic[index] = bsic | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + } + + if ((sb_det == 2)||(sb_det == 4)) { + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50); + l1s.tpu_offset = old_tpu_offset; + //printf("afc_correct (-ve) in l1s_neigh_fbsb_resp for SB - nb_fb_angle0+nb_fb_angle1\n\n"); + afc_correct(-1*(ANGLE_TO_FREQ(nb_fb_angle0) + ANGLE_TO_FREQ(nb_fb_angle1)), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + } + + if ((sb_det == 4)||(sb_found == 1)) { + l1s.neigh_sb.count = 0; + //MTZ - need to change this statement based on detection + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + + l1s.neigh_sb.running = 0; + + //dsp_api.r_page_used = 1; + + if (sb_found == 0) + printf("\n\n"); + } + + } + + if ((sb_det == 4)||(sb_found == 1)) + sb_det = 0; + else + sb_det++; + } + + return 0; +} + +//MTZ - THIS FUNCTION BELOW IS NOT USED!!!!! +static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_sb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; } +//MTZ - THIS FUNCTION BELOW IS NOT USED!!!!! +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + printf("\nMTZ: In l1s_neigh_sb_resp, l1s.serving_cell.arfcn = %d\n", l1s.serving_cell.arfcn); + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_sync, 1, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) { l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; diff --git a/src/target/firmware/layer1/prim_fbsbSynchUsingDedModeDSPTasksInIdleCopy.c b/src/target/firmware/layer1/prim_fbsbSynchUsingDedModeDSPTasksInIdleCopy.c new file mode 100644 index 00000000..6139629d --- /dev/null +++ b/src/target/firmware/layer1/prim_fbsbSynchUsingDedModeDSPTasksInIdleCopy.c @@ -0,0 +1,1559 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +static int fb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added +int total_sb_det = 0; +int attempt2 = 0; + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle0 = 0; //MTZ - This was added +uint16_t nb_fb_angle1 = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +// MTZ - for working of these variables see comments in l1s_neigh_fbsb_cmd +// MTZ - det_serving_cell overrides neigh_for_fbsb_det +int synchronize_yes = 0; //MTZ - A test variable +int det_serving_cell = 0; //MTZ - A test variable +int neigh_for_fbsb_det = 0; //MTZ - A test variable + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + printf("\n\nMTZ: time->fn = %d\n\n", time->fn); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +static void read_sb_result2(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + //MTZ - commenting out for now + //if (st->snr > AFC_SNR_THRESHOLD) + // afc_input(st->freq_diff, rf_arfcn, 1); + //else + // afc_input(st->freq_diff, rf_arfcn, 0); + + //dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + printf("\n\nMTZ: current_fn = %d, fn from SB = %d\n\n", gsm_gsmtime2fn(&l1s.current_time), fbs.mon.time.fn + SB2_LATENCY); + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + printf("\nMTZ: l1s.serving_cell.ccch_mode = %d\n", l1s.serving_cell.ccch_mode); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_sbdet_cmd = %d\n", rf_arfcn); + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +static int l1s_sbdet_resp2(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.ndb->a_sch26[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + attempt2++; + + tdma_schedule_set(0, sb_sched_set, 0); + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + attempt2=0; + + printf("SB%d ", attempt); + read_sb_result2(last_fb, attempt); + + sb = dsp_api.ndb->a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + printf("\n\nMTZ: current_fn = %d, fn from SB = %d\n\n", gsm_gsmtime2fn(&l1s.current_time), fbs.mon.time.fn + SB2_LATENCY); + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + printf("\nMTZ: l1s.serving_cell.ccch_mode = %d\n", l1s.serving_cell.ccch_mode); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd2(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_sbdet_cmd2 = %d\n", rf_arfcn); + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB26, 0); + + return 0; +} + +///* This is how it is done by the TSM30 */ +//static const struct tdma_sched_item sb_sched_set[] = { +// SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), +// SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd2, 0, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp2, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_fbdet_cmd = %d\n", fbs.req.band_arfcn); + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd2(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_det2; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_det2; + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_fbdet_cmd2 = %d\n", fbs.req.band_arfcn); + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB26, 0); + + return 0; +} + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp2(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt2 < 30) + tdma_schedule_set(0, fb_sched_set, 0); + + attempt2++; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + + return 0; + } + + attempt2 = 0; + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_det2 == 0) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + //if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + // last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + fb_det2 = 1; + tdma_schedule_set(0, fb_sched_set, 0); + //} else { + // /* Abort */ + // last_fb->attempt = 13; + // l1s_compl_sched(L1_COMPL_FB); + //} + } else if (fb_det2 == 1) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + //int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + //int delay = fn_offset + 11 - l1s.current_time.fn - 1; + //printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + // fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + //printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + // fn_offset, l1s.current_time.fn); + //printf(" scheduling next FB/SB detection task with delay %u\n", delay); + //if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + // last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + //tdma_schedule_set(delay, sb_sched_set, 0); + tdma_schedule_set(0, sb_sched_set, 0); + //} else + // tdma_schedule_set(delay, fb_sched_set, 1); + } + + return 0; +} + +///* FB detection */ +//static const struct tdma_sched_item fb_sched_set[] = { +// SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), +// SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd2, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp2, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + + fb_det2 = 0; + tdma_schedule_set(0, fb_sched_set, 0); + + //MTZ - Changed + //if (fbs.req.flags & L1CTL_FBSB_F_FB0) + // tdma_schedule_set(base_fn, fb_sched_set, 0); + //else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + // tdma_schedule_set(base_fn, fb_sched_set, 0); + //else if (fbs.req.flags & L1CTL_FBSB_F_SB) + // tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test1(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + printf("\n\nMTZ - sync_test1, old_tpu_offset = %d\n\n", l1s.tpu_offset); + + old_tpu_offset = l1s.tpu_offset; + + //l1s.tpu_offset = 3000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + //tpu_enq_at(0); + //tpu_enq_at(0); + //l1s.tpu_offset = old_tpu_offset; + //l1s.tpu_offset = 2000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + + //tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(3000); + //tpu_enq_at(0); + //tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + //tpu_enq_at(0); + + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(2000); + ////tpu_enq_at(0); + ////tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + + printf("\n\nMTZ - sync_test1, ending tpu_offset = %d\n\n", l1s.tpu_offset); + + +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test2(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + printf("\n\nMTZ - sync_test2\n\n"); + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_sync(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + if (fb_det == 1) { + afc_correct(ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + } + + if ((sb_det == 2)||(sb_det == 4)) { + //printf("\nMTZ - in l1s_neigh_fbsb_sync, old_tpu_offset = %d\n", l1s.tpu_offset); + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; //MTZ - uncomment + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; //MTZ - uncomment + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + //printf("MTZ - old_tpu_offset = %d, tpu_shift = %d, qbits = %d\n", old_tpu_offset, tpu_shift, qbits); + + l1s.neigh_pm.tpu_offset[l1s.neigh_sb.index] = tpu_shift; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle0)+ANGLE_TO_FREQ(nb_fb_angle1); + break; + } + if (l1s.tpu_offsets_arfcn[ii] == 0) { + l1s.tpu_offsets_arfcn[ii] = l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]; + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle0)+ANGLE_TO_FREQ(nb_fb_angle1); + break; + } + } + + printf("\n\nMTZ: Stored TPU Offsets, Angles:"); + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == 0) + break; + printf(" %d,%d(%d)", l1s.tpu_offsets[ii], l1s.nb_freq_diff[ii], l1s.tpu_offsets_arfcn[ii]); + } + printf("\n\n"); + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50) { + l1s.tpu_offset = tpu_shift; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(tpu_shift); + } + afc_correct(ANGLE_TO_FREQ(nb_fb_angle1)+ANGLE_TO_FREQ(nb_fb_angle1), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + +} +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + //printf("\nMTZ: In neigh_fbsb_cmd, l1s.neigh_pm.n = %d", l1s.neigh_pm.n); + + //---------------------------------------------------------------------------------------- + // + // Important points of note: + // ------------------------- + // + // Temporary variables used for testing: + // + // Right now we have three global variables defined at the beginning of this file + // for testing purposes: det_serving_cell, synchronize_yes and neigh_for_fbsb_det. + // + // det_serving_cell allows for detection of FBSB on serving cell, something which we + // thought should be simpler as the clock is already synched on that cell. + // + // synchronize_yes is a variable with which one can control whether offset and synch + // commands will be sent to the TPU. There was a thought that perhaps the DSP SB + // detection task for dedicated mode might not require synching due to which this + // test variable was created. Also, naturally, detection on the serving cell should + // not require synching but we could be wrong. + // + // neigh_for_fbsb_det is a variable to hardcode the neighbour one wants to detect + // FBSB on. Note that because more functionality is added to the code the mechanism + // for traversing through neighbours using select_neigh_cell and certain flags is + // disturbed for now. Due to this the index is hardcoded in the code below to + // whichever neighbour one wants to detect FBSB on (ordered by signal strength) + // using this variable. + // + // Progress so far: + // + // Right now we are able to detect FB in dedicated mode even for neighbours. One + // additional thing we have done to aid us is that we have added 3 more frames to + // our idle frame by taking up some TCH channels (this can be seen in mframe_sched). + // The call doesn't get dropped if synchronization is not performed except for very + // few cases which we need not worry about for now. However, when synchronization + // is performed the call gets dropped without exception. + // + // Few points where the problem could lie: + // + // 1) Perhaps we are not using the TCH_SB_DSP_TASK correctly. Perhaps some other + // locations in the API also need to be written to when issuing the command in + // addition to dsp_api.db_w->d_task_md. Perhaps we are not reading from the correct + // location when checking the response. + // + // 2) Perhaps we need to start the SB detection task some quarter-bits or timeslots + // before the actual SB appears. + // + // 3) Timing issues. This is the most likely cause as we haven't really been able + // to fully understand and implement timing (if it is required, which it seems like + // it is) in the code. If timing is required then this code is quite superficial. + // In idle mode functions such as afc_correct and procedures requiring frequency + // difference are used to before calling SB detection task which are not done here. + // + // Timing issues seems to be the most likely problem. Anyone wishing to solve the + // SB detection issue should try to focus on this problem, understand how + // synchronization is performed in idle mode and how we can do that in dedicated + // mode and be back within 4 frames as after that the traffic needs to resume. + // + //---------------------------------------------------------------------------------------- + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + +// //MTZ - putting this for now as we wanted to repeatedly detect the remaining ones - remove +// while (!((l1s.neigh_sb.flags_bsic[index] & NEIGH_PM_FLAG_BSIC) == 0)) { +//// printf("\nMTZ: BSIC has been decoded for ARFCN %d (flags_bsic[%d] = %d)\n\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.flags_bsic[index]); +// l1s.neigh_sb.count = 0; +// l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; +// select_neigh_cell(); +// index = l1s.neigh_sb.index; +// } + + //MTZ - This index variable is used to hardcode the neighbour cell for now + //index = neigh_for_fbsb_det; + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + +// if (det_serving_cell) +// printf(" - detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + if (fb_det == 1) { + dsp_api.ndb->d_fb_mode = 1; + } else { + dsp_api.ndb->d_fb_mode = 0; + } + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (FB%d) = %d\n", fb_det, l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if ((sb_det == 2)||(sb_det == 4)) { + +// if (det_serving_cell) +// printf(" - detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + //MTZ - This is a variable for the testing phase whether to send sync commands or not + //if (synchronize_yes) + // synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (SB) = %d\n", l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + uint8_t bsic; + int sb_found = 0; + + if (sb_det == 0) { + if (fb_det == 1) + afc_correct(-1*ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + if (!dsp_api.ndb->d_fb_det) { + printf("MTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 0\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det); + + /* next sync */ + if (++l1s.neigh_sb.count == 15) { + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + if (fb_det == 1){ + fb_det = 0; + } + } + + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + if (fb_det == 1) + nb_fb_angle1 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + else + nb_fb_angle0 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\n\nMTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 1 >>> nb_fb_toa = %d, angle = %d\n\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det, nb_fb_toa, dsp_api.ndb->a_sync_demod[D_ANGLE]); + if (fb_det == 0) { + fb_det = 1; + l1s.neigh_sb.count = 1; + } else { + sb_det = 1; + fb_det = 0; + } + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + if ((sb_det == 2)||(sb_det == 4)) { + /* check if sync was successful */ + //if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + bsic = (sb >> 2) & 0x3f; + + t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + fn = (51 * ((t3 - t2 + 26) % 26) + t3 + (26 * 51 * t1)) + SB2_LATENCY; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.nb_frame_diff[ii] = fn - l1s.current_time.fn; + break; + } + } + + + total_sb_det++; + //printf("=> SB 0x%08"PRIx32": BSIC=%u \n\n", sb, bsic); + printf("\n----------------------------------------------------------------------------\nSB found = 1 (ARFCN %d, power %d dbm) => SB 0x%08"PRIx32": BSIC=%u, TOA=%d, Angle=%d (Total=%d)\n----------------------------------------------------------------------------\n\n", l1s.neigh_pm.band_arfcn[index], rxlev2dbm(l1s.neigh_pm.level[index]), sb, bsic, dsp_api.db_r->a_serv_demod[D_TOA], dsp_api.db_r->a_serv_demod[D_ANGLE], total_sb_det); + l1s.neigh_sb.flags_bsic[index] = bsic | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + //if ((l1s.new_dm == 1) && (l1s.neigh_pm.band_arfcn[index] != 58)) { + // l1s.ho_arfcn = l1s.neigh_pm.band_arfcn[index]; + // l1s.new_dm = 0; + //} + } + + if ((sb_det == 2)||(sb_det == 4)) { + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50); + l1s.tpu_offset = old_tpu_offset; + afc_correct(-1*(ANGLE_TO_FREQ(nb_fb_angle0) + ANGLE_TO_FREQ(nb_fb_angle1)), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + + if ((sb_det == 4)||(sb_found == 1)) { + l1s.neigh_sb.count = 0; + //MTZ - need to change this statement based on detection + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + + l1s.neigh_sb.running = 0; + + //dsp_api.r_page_used = 1; + + if (sb_found == 0) + printf("\n\n"); + } + + } + + if ((sb_det == 4)||(sb_found == 1)) + sb_det = 0; + else + sb_det++; + } + + return 0; +} + +static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_sb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + printf("\nMTZ: In l1s_neigh_sb_resp, l1s.serving_cell.arfcn = %d\n", l1s.serving_cell.arfcn); + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_sync, 1, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(sync_test1, 1, 0, 1), SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_ITEM_DT(sync_test2, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/prim_fbsbcurr.c b/src/target/firmware/layer1/prim_fbsbcurr.c new file mode 100644 index 00000000..275db9dc --- /dev/null +++ b/src/target/firmware/layer1/prim_fbsbcurr.c @@ -0,0 +1,1223 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added +int total_sb_det = 0; + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +// MTZ - for working of these variables see comments in l1s_neigh_fbsb_cmd +// MTZ - det_serving_cell overrides neigh_for_fbsb_det +int synchronize_yes = 0; //MTZ - A test variable +int det_serving_cell = 0; //MTZ - A test variable +int neigh_for_fbsb_det = 0; //MTZ - A test variable + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + printf("\nMTZ: l1s.serving_cell.ccch_mode = %d\n", l1s.serving_cell.ccch_mode); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_sbdet_cmd = %d\n", rf_arfcn); + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_fbdet_cmd = %d\n", fbs.req.band_arfcn); + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test1(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + printf("\n\nMTZ - sync_test1, old_tpu_offset = %d\n\n", l1s.tpu_offset); + + old_tpu_offset = l1s.tpu_offset; + + //l1s.tpu_offset = 3000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + //tpu_enq_at(0); + //tpu_enq_at(0); + //l1s.tpu_offset = old_tpu_offset; + //l1s.tpu_offset = 2000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + + //tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(3000); + //tpu_enq_at(0); + //tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + //tpu_enq_at(0); + + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(2000); + ////tpu_enq_at(0); + ////tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + + printf("\n\nMTZ - sync_test1, ending tpu_offset = %d\n\n", l1s.tpu_offset); + + +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test2(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + printf("\n\nMTZ - sync_test2\n\n"); + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_sync(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + if ((sb_det == 2)||(sb_det == 4)) { + //printf("\nMTZ - in l1s_neigh_fbsb_sync, old_tpu_offset = %d\n", l1s.tpu_offset); + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; //MTZ - uncomment + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; //MTZ - uncomment + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + printf("MTZ - old_tpu_offset = %d, tpu_shift = %d, qbits = %d\n", old_tpu_offset, tpu_shift, qbits); + + l1s.neigh_pm.tpu_offset[l1s.neigh_sb.index] = tpu_shift; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle); + break; + } + if (l1s.tpu_offsets_arfcn[ii] == 0) { + l1s.tpu_offsets_arfcn[ii] = l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]; + l1s.tpu_offsets[ii] = tpu_shift; + l1s.nb_freq_diff[ii] = ANGLE_TO_FREQ(nb_fb_angle); + break; + } + } + + printf("\n\nMTZ: Stored TPU Offsets, Angles:"); + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == 0) + break; + printf(" %d,%d(%d)", l1s.tpu_offsets[ii], l1s.nb_freq_diff[ii], l1s.tpu_offsets_arfcn[ii]); + } + printf("\n\n"); + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50) { + l1s.tpu_offset = tpu_shift; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(tpu_shift); + } + afc_correct(ANGLE_TO_FREQ(nb_fb_angle), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + +} +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + //printf("\nMTZ: In neigh_fbsb_cmd, l1s.neigh_pm.n = %d", l1s.neigh_pm.n); + + //---------------------------------------------------------------------------------------- + // + // Important points of note: + // ------------------------- + // + // Temporary variables used for testing: + // + // Right now we have three global variables defined at the beginning of this file + // for testing purposes: det_serving_cell, synchronize_yes and neigh_for_fbsb_det. + // + // det_serving_cell allows for detection of FBSB on serving cell, something which we + // thought should be simpler as the clock is already synched on that cell. + // + // synchronize_yes is a variable with which one can control whether offset and synch + // commands will be sent to the TPU. There was a thought that perhaps the DSP SB + // detection task for dedicated mode might not require synching due to which this + // test variable was created. Also, naturally, detection on the serving cell should + // not require synching but we could be wrong. + // + // neigh_for_fbsb_det is a variable to hardcode the neighbour one wants to detect + // FBSB on. Note that because more functionality is added to the code the mechanism + // for traversing through neighbours using select_neigh_cell and certain flags is + // disturbed for now. Due to this the index is hardcoded in the code below to + // whichever neighbour one wants to detect FBSB on (ordered by signal strength) + // using this variable. + // + // Progress so far: + // + // Right now we are able to detect FB in dedicated mode even for neighbours. One + // additional thing we have done to aid us is that we have added 3 more frames to + // our idle frame by taking up some TCH channels (this can be seen in mframe_sched). + // The call doesn't get dropped if synchronization is not performed except for very + // few cases which we need not worry about for now. However, when synchronization + // is performed the call gets dropped without exception. + // + // Few points where the problem could lie: + // + // 1) Perhaps we are not using the TCH_SB_DSP_TASK correctly. Perhaps some other + // locations in the API also need to be written to when issuing the command in + // addition to dsp_api.db_w->d_task_md. Perhaps we are not reading from the correct + // location when checking the response. + // + // 2) Perhaps we need to start the SB detection task some quarter-bits or timeslots + // before the actual SB appears. + // + // 3) Timing issues. This is the most likely cause as we haven't really been able + // to fully understand and implement timing (if it is required, which it seems like + // it is) in the code. If timing is required then this code is quite superficial. + // In idle mode functions such as afc_correct and procedures requiring frequency + // difference are used to before calling SB detection task which are not done here. + // + // Timing issues seems to be the most likely problem. Anyone wishing to solve the + // SB detection issue should try to focus on this problem, understand how + // synchronization is performed in idle mode and how we can do that in dedicated + // mode and be back within 4 frames as after that the traffic needs to resume. + // + //---------------------------------------------------------------------------------------- + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + +// //MTZ - putting this for now as we wanted to repeatedly detect the remaining ones - remove +// while (!((l1s.neigh_sb.flags_bsic[index] & NEIGH_PM_FLAG_BSIC) == 0)) { +//// printf("\nMTZ: BSIC has been decoded for ARFCN %d (flags_bsic[%d] = %d)\n\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.flags_bsic[index]); +// l1s.neigh_sb.count = 0; +// l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; +// select_neigh_cell(); +// index = l1s.neigh_sb.index; +// } + + //MTZ - This index variable is used to hardcode the neighbour cell for now + //index = neigh_for_fbsb_det; + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + +// if (det_serving_cell) +// printf(" - detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (FB) = %d\n", l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if ((sb_det == 2)||(sb_det == 4)) { + +// if (det_serving_cell) +// printf(" - detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + //MTZ - This is a variable for the testing phase whether to send sync commands or not + //if (synchronize_yes) + // synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (SB) = %d\n", l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + uint8_t bsic; + int sb_found = 0; + + if (sb_det == 0) { + if (!dsp_api.ndb->d_fb_det) { +// printf("MTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB found = 0\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count); + + /* next sync */ + if (++l1s.neigh_sb.count == 11) { + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + } + + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + nb_fb_angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\n\nMTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB found = 1 >>> nb_fb_toa = %d\n\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, nb_fb_toa); + sb_det = 1; + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + + if ((sb_det == 2)||(sb_det == 4)) { + /* check if sync was successful */ + //if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + bsic = (sb >> 2) & 0x3f; + total_sb_det++; + //printf("=> SB 0x%08"PRIx32": BSIC=%u \n\n", sb, bsic); + printf("\n----------------------------------------------------------------------------\nSB found = 1 (ARFCN %d, power %d dbm) => SB 0x%08"PRIx32": BSIC=%u, TOA=%d (Total=%d)\n----------------------------------------------------------------------------\n\n", l1s.neigh_pm.band_arfcn[index], rxlev2dbm(l1s.neigh_pm.level[index]), sb, bsic, dsp_api.db_r->a_serv_demod[D_TOA], total_sb_det); + l1s.neigh_sb.flags_bsic[index] = bsic | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + if ((l1s.new_dm == 1) && (l1s.neigh_pm.band_arfcn[index] != 58)) { + l1s.ho_arfcn = l1s.neigh_pm.band_arfcn[index]; + l1s.new_dm = 0; + } + } + + if ((sb_det == 2)||(sb_det == 4)) { + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50); + l1s.tpu_offset = old_tpu_offset; + afc_correct(-1*ANGLE_TO_FREQ(nb_fb_angle), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + + if ((sb_det == 4)||(sb_found == 1)) { + l1s.neigh_sb.count = 0; + //MTZ - need to change this statement based on detection + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + if (sb_found == 0) + printf("\n\n"); + } + + } + + if ((sb_det == 4)||(sb_found == 1)) + sb_det = 0; + else + sb_det++; + } + + return 0; +} + +static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_sb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + printf("\nMTZ: In l1s_neigh_sb_resp, l1s.serving_cell.arfcn = %d\n", l1s.serving_cell.arfcn); + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_sync, 1, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(sync_test1, 1, 0, 1), SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_ITEM_DT(sync_test2, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c index 5c8c4914..6f079048 100644 --- a/src/target/firmware/layer1/prim_pm.c +++ b/src/target/firmware/layer1/prim_pm.c @@ -156,11 +156,43 @@ void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) } /* - * perform measurements of neighbour cells + * perform measurements of neighbour cells on idle frame */ +/* send measurement results */ +static void neigh_pm_ind(void) +{ + struct msgb *msg; + struct l1ctl_neigh_pm_ind *mi; + int i; + uint8_t half_rounds = l1s.neigh_pm.rounds >> 1; + + /* return result */ + msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (msgb_tailroom(msg) < (int) sizeof(*mi)) { + l1_queue_for_l2(msg); + msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); + } + mi = (struct l1ctl_neigh_pm_ind *) + msgb_put(msg, sizeof(*mi)); + mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]); + mi->tn = l1s.neigh_pm.tn[i]; + l1s.neigh_pm.level[i] + = (l1s.neigh_pm.level_sum[i] + half_rounds) + / l1s.neigh_pm.rounds; + mi->pm[0] = l1s.neigh_pm.level[i]; + l1s.neigh_pm.level_sum[i] = 0; + if ((l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_BSIC)) + mi->bsic = l1s.neigh_sb.flags_bsic[i] & 0x3f; + else + mi->bsic = 255; + } + l1_queue_for_l2(msg); +} + /* scheduler callback to issue a power measurement task to the DSP */ -static int l1s_neigh_pm_cmd(uint8_t num_meas, +static int l1s_neigh_pm_idle_cmd(uint8_t num_meas, __unused uint8_t p2, __unused uint16_t p3) { uint8_t last_gain = rffe_get_gain(); @@ -188,7 +220,7 @@ static int l1s_neigh_pm_cmd(uint8_t num_meas, } /* scheduler callback to read power measurement resposnse from the DSP */ -static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2, +static int l1s_neigh_pm_idle_resp(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) { uint16_t dbm; @@ -202,29 +234,140 @@ static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2, dbm = (uint16_t) ((dsp_api.db_r->a_pm[0] & 0xffff) >> 3); level = dbm2rxlev(agc_inp_dbm8_by_pm(dbm)/8); - l1s.neigh_pm.level[l1s.neigh_pm.pos] = level; + l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] = level; if (++l1s.neigh_pm.pos >= l1s.neigh_pm.n) { - struct msgb *msg; - struct l1ctl_neigh_pm_ind *mi; - int i; - l1s.neigh_pm.pos = 0; - /* return result */ - msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); - for (i = 0; i < l1s.neigh_pm.n; i++) { - if (msgb_tailroom(msg) < (int) sizeof(*mi)) { - l1_queue_for_l2(msg); - msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); + l1s.neigh_pm.rounds++; + neigh_pm_ind(); + l1s.neigh_pm.rounds = 0; + l1s.neigh_pm.valid = 1; + } + +out: + l1s.neigh_pm.running = 0; + + return 0; +} + +const struct tdma_sched_item neigh_pm_idle_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_pm_idle_cmd, -1, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_pm_idle_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* + * Perform measurements of neighbour cells on TCH + * + * Only when number of neighbor cells is > 0, perform measurement. + * + * For each measurement, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending measurement would + * be ignored. + * + * The measuement starts at position 0 (first neighbor cell). After each + * measurement result, the position is incremented until the number of neighbor + * cells are reached. + * + * All measurement results are added to level_sum array. It will be used to + * calculate an average from multiple measurements. + * + * All measurements start at round 0. When all neighbors have been measured, + * the number of rounds is increased and the measurement start over. At start + * of round 0, the start_fn is recorded. It will be used to calculate the + * elapsed time from the beginning. + * + * After reach round, the number of elapsed frames is checked. If at least 104 + * frames have been elapsed, this would be the last round. The average of + * measurements are calculated from the level_sum array. Then the result is + * indicated to layer, the measurement states are reset, the measurments are + * maked as valid, and the measurement process starts over. + */ + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_neigh_pm_tch_cmd(uint8_t num_meas, + __unused uint8_t p2, __unused uint16_t p3) +{ + uint8_t last_gain; + + if (l1s.neigh_pm.n == 0) + return 0; + + /* set start_fn for the first round */ + if (l1s.neigh_pm.pos == 0 && l1s.neigh_pm.rounds == 0) + l1s.neigh_pm.start_fn = l1s.next_time.fn; + + /* save current gain */ + last_gain = rffe_get_gain(); + + dsp_api.db_w->d_task_md = num_meas; /* number of measurements */ +// dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Tell the RF frontend to set the gain appropriately (keep last) */ + rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL); + + + /* + * Program TPU + * Use TS 5 (two TS after TX) + */ + l1s_rx_win_ctrl((l1s.neigh_pm.n) ? + l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] : 0, + L1_RXWIN_PW, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_pm.running = 1; + + return 0; +} + +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_neigh_pm_tch_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + uint16_t dbm; + uint8_t level; + + dsp_api.r_page_used = 1; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_pm.running) + goto out; + + dbm = (uint16_t) ((dsp_api.db_r->a_pm[0] & 0xffff) >> 3); + level = dbm2rxlev(agc_inp_dbm8_by_pm(dbm)/8); + + //MTZ: The code below is to artificially induce handover + ////////////////////////////////////////////// + if (((l1s.serving_cell.arfcn == 25) && ((l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 57)||(l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 52)||(l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 49))) || ((l1s.serving_cell.arfcn == 57) && ((l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 25)||(l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 52)||(l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] == 58)))) { + l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] += 63; + } else { + l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] += level; + } + /////////////////////////////////////////////// + + + //MTZ: This if statement is used because it was seen that sometimes a level of 0 was read off in dedicated mode which is not valid + if (level > 0) { + if (++l1s.neigh_pm.pos >= l1s.neigh_pm.n) { + uint32_t elapsed = (l1s.next_time.fn + 2715648 + - l1s.neigh_pm.start_fn) % 2715648; + + l1s.neigh_pm.pos = 0; + l1s.neigh_pm.rounds++; + /* + * We want at least 104 frames before indicating the + * measurement(s). Add two, since the measurement was + * started two frames ago. + */ + if (elapsed >= 104 + 2) { + neigh_pm_ind(); + l1s.neigh_pm.rounds = 0; + l1s.neigh_pm.valid = 1; } - mi = (struct l1ctl_neigh_pm_ind *) - msgb_put(msg, sizeof(*mi)); - mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]); - mi->tn = l1s.neigh_pm.tn[i]; - mi->pm[0] = l1s.neigh_pm.level[i]; - mi->pm[1] = 0; } - l1_queue_for_l2(msg); } out: @@ -233,10 +376,11 @@ out: return 0; } -const struct tdma_sched_item neigh_pm_sched_set[] = { - SCHED_ITEM_DT(l1s_neigh_pm_cmd, 0, 1, 0), SCHED_END_FRAME(), +/* NOTE: Prio 1 is below TCH's TX+RX prio 0 */ +const struct tdma_sched_item neigh_pm_tch_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_pm_tch_cmd, 1, 1, 0), SCHED_END_FRAME(), SCHED_END_FRAME(), - SCHED_ITEM(l1s_neigh_pm_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_pm_tch_resp, -4, 1, 0), SCHED_END_FRAME(), SCHED_END_SET() }; diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c index e6ea6568..97736ec9 100644 --- a/src/target/firmware/layer1/prim_rach.c +++ b/src/target/firmware/layer1/prim_rach.c @@ -50,6 +50,10 @@ #include +int hando_access_flag = 0; +int16_t hando_arfcn = 0; +int ho_flag = 0; + struct { uint32_t fn; uint16_t band_arfcn; @@ -60,11 +64,18 @@ static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused ui { uint16_t *info_ptr; uint16_t arfcn; + uint8_t tsc, tn; uint8_t data[2]; putchart('T'); - l1s_tx_apc_helper(l1s.serving_cell.arfcn); + if (hando_access_flag == 1) + arfcn = hando_arfcn; + else + arfcn = l1s.serving_cell.arfcn; + + //l1s_tx_apc_helper(l1s.serving_cell.arfcn); + l1s_tx_apc_helper(arfcn); data[0] = l1s.serving_cell.bsic << 2; data[1] = l1s.rach.ra; @@ -72,7 +83,11 @@ static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused ui info_ptr = &dsp_api.ndb->d_rach; info_ptr[0] = ((uint16_t)(data[0])) | ((uint16_t)(data[1])<<8); - arfcn = l1s.serving_cell.arfcn; + //rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); //MTZ - ADDED LATER - POSSIBLY REMOVE + + //hando_access_flag = 0; + + printf("\n\nMTZ - arfcn in l1s_tx_rach_cmd = %d, serving cell arfcn = %d\n\n", arfcn, l1s.serving_cell.arfcn); dsp_api.db_w->d_task_ra = dsp_task_iq_swap(RACH_DSP_TASK, arfcn, 1); @@ -85,14 +100,50 @@ static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused ui static int l1s_tx_rach_resp(__unused uint8_t p1, __unused uint8_t burst_id, __unused uint16_t p3) { + uint16_t arfcn; + + if (hando_access_flag == 1) + arfcn = hando_arfcn; + else + arfcn = l1s.serving_cell.arfcn; + + hando_access_flag = 0; + putchart('t'); dsp_api.r_page_used = 1; + if (ho_flag == 1) { + ho_flag = 0; + int ii = 0; + for (ii =0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == arfcn) { + if (l1s.nb_sb_snr[ii] > 0) + afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 1); + else + afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 0); + + + //mframe_disable(MF_TASK_TCH_F_EVEN); + //mframe_disable(MF_TASK_TCH_F_ODD); + //mframe_disable(MF_TASK_TCH_H_0); + //mframe_disable(MF_TASK_TCH_H_1); + + //mframe_disable(MF_TASK_NEIGH_PM51_C0T0); + //mframe_disable(MF_TASK_NEIGH_PM51); + //mframe_disable(MF_TASK_NEIGH_PM26E); + //mframe_disable(MF_TASK_NEIGH_PM26O); + + //mframe_enable(MF_TASK_BCCH_NORM); + + } + } + } + /* schedule a confirmation back indicating the GSM time at which * the RACH burst was transmitted to the BTS */ last_rach.fn = l1s.current_time.fn - 1; - last_rach.band_arfcn = l1s.serving_cell.arfcn; + last_rach.band_arfcn = arfcn; l1s_compl_sched(L1_COMPL_RACH); return 0; @@ -111,6 +162,7 @@ static void l1a_rach_compl(__unused enum l1_compl c) { struct msgb *msg; + printf("\n\nMTZ: L1CTL_RACH_CONF message being sent!\n\n\n"); msg = l1_create_l2_msg(L1CTL_RACH_CONF, last_rach.fn, 0, last_rach.band_arfcn); l1_queue_for_l2(msg); @@ -131,16 +183,72 @@ static uint8_t rach_to_t3_comb[27] = { 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 45, 46}; -/* request a RACH request at the next multiframe T3 = fn51 */ -void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) +/* schedule access burst */ +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint16_t arfcn) { uint32_t fn_sched; unsigned long flags; + if (arfcn > 0) { + hando_arfcn = arfcn; + hando_access_flag = 1; + //printf("\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nMTZ: CP1\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n"); + uint32_t new_fn; + if (l1s.new_dm) { + int ii = 0; + for (ii =0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == arfcn) { + printf("\n\n\n\n\n\n\n-------------------------------------------\n\n\n\n\nMTZ: SYNCHING TO NEW ARFCN %d\n\n\n\n\n-------------------------------------------\n\n\n\n\n\n\n\n", arfcn); + //printf("\n\n\n\n\n\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!MTZ: SYNCHING TO NEW ARFCN %d (offset=%d, freq_diff=%d, sb_fb_freq_diff=%d, sb_snr=%d, frame_diff=%d)!!!!!!!!!\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n\n\n\n\n\n\n", arfcn, l1s.tpu_offsets[ii], l1s.nb_freq_diff[ii], l1s.nb_sb_freq_diff[ii], l1s.nb_sb_snr[ii], l1s.nb_frame_diff[ii]); + + //MTZ - Setting tpu_offset for frame start synchronization as well as doing frequency compensation + l1s.orig_tpu_offset = l1s.tpu_offset; + l1s.tpu_offset = l1s.tpu_offsets[ii]; + afc_correct(l1s.nb_freq_diff[ii], arfcn); + + //if (l1s.nb_sb_snr[ii] > 0) + // afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 1); + //else + // afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 0); + + //MTZ - Setting frame number using the difference computed before + new_fn = l1s.current_time.fn + l1s.nb_frame_diff[ii]; + while (new_fn < 0) + new_fn += 2715647; + new_fn = new_fn % 2715647; + gsm_fn2gsmtime(&l1s.current_time, new_fn); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + tdma_sched_reset(); + + ho_flag = 1; + + } + } + l1s.new_dm = 0; + } + } + offset += 3; + //printf("\n\nMTZ: In l1a_rach_req, serving cell = %d\n\n", l1s.serving_cell.arfcn); + local_firq_save(flags); - if (combined) { + if (l1s.dedicated.type == GSM_DCHAN_TCH_F) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame TCH/F channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + } else if (l1s.dedicated.type == GSM_DCHAN_TCH_H) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame of TCH/H channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + if ((l1s.dedicated.chan_nr & 1) != ((fn_sched % 13) & 1)) + fn_sched++; + } else if (combined) { /* add elapsed RACH slots to offset */ offset += t3_to_rach_comb[l1s.current_time.t3]; /* offset is the number of RACH slots in the future */ @@ -150,6 +258,7 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) } else fn_sched = l1s.current_time.fn + offset; l1s.rach.ra = ra; + fn_sched %= 2715648; sched_gsmtime(rach_sched_set_ul, fn_sched, 0); local_irq_restore(flags); diff --git a/src/target/firmware/layer1/prim_rach_tmp.c b/src/target/firmware/layer1/prim_rach_tmp.c new file mode 100644 index 00000000..e2ab9488 --- /dev/null +++ b/src/target/firmware/layer1/prim_rach_tmp.c @@ -0,0 +1,265 @@ +/* Layer 1 Random Access Channel Burst */ + +/* (C) 2010 by Dieter Spaar + * (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int hando_access_flag = 0; +int16_t hando_arfcn = 0; +int ho_flag = 0; + +struct { + uint32_t fn; + uint16_t band_arfcn; +} last_rach; + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + uint16_t *info_ptr; + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t data[2]; + + putchart('T'); + + if (hando_access_flag == 1) + arfcn = hando_arfcn; + else + arfcn = l1s.serving_cell.arfcn; + + //l1s_tx_apc_helper(l1s.serving_cell.arfcn); + l1s_tx_apc_helper(arfcn); + + data[0] = l1s.serving_cell.bsic << 2; + data[1] = l1s.rach.ra; + + info_ptr = &dsp_api.ndb->d_rach; + info_ptr[0] = ((uint16_t)(data[0])) | ((uint16_t)(data[1])<<8); + + //rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); //MTZ - ADDED LATER - POSSIBLY REMOVE + + //hando_access_flag = 0; + + printf("\n\nMTZ - arfcn in l1s_tx_rach_cmd = %d, serving cell arfcn = %d\n\n", arfcn, l1s.serving_cell.arfcn); + + dsp_api.db_w->d_task_ra = dsp_task_iq_swap(RACH_DSP_TASK, arfcn, 1); + + l1s_tx_win_ctrl(arfcn | ARFCN_UPLINK, L1_TXWIN_AB, 0, 3); + + return 0; +} + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_resp(__unused uint8_t p1, __unused uint8_t burst_id, + __unused uint16_t p3) +{ + uint16_t arfcn; + + if (hando_access_flag == 1) + arfcn = hando_arfcn; + else + arfcn = l1s.serving_cell.arfcn; + + hando_access_flag = 0; + + putchart('t'); + + dsp_api.r_page_used = 1; + + if (ho_flag == 1) { + ho_flag = 0; + int ii = 0; + for (ii =0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == arfcn) { + if (l1s.nb_sb_snr[ii] > 0) + afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 1); + else + afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 0); + } + } + } + + /* schedule a confirmation back indicating the GSM time at which + * the RACH burst was transmitted to the BTS */ + last_rach.fn = l1s.current_time.fn - 1; + last_rach.band_arfcn = arfcn; + l1s_compl_sched(L1_COMPL_RACH); + + return 0; +} + +/* sched sets for uplink */ +const struct tdma_sched_item rach_sched_set_ul[] = { + SCHED_ITEM_DT(l1s_tx_rach_cmd, 3, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_rach_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_rach_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + printf("\n\nMTZ: L1CTL_RACH_CONF message being sent!\n\n\n"); + msg = l1_create_l2_msg(L1CTL_RACH_CONF, last_rach.fn, 0, + last_rach.band_arfcn); + l1_queue_for_l2(msg); +} + +static uint8_t t3_to_rach_comb[51] = { + 0, 0, 0, 0, + 0, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 26, + 27, 27, 27, 27}; +static uint8_t rach_to_t3_comb[27] = { + 4, 5, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 45, 46}; + +/* schedule access burst */ +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint16_t arfcn) +{ + uint32_t fn_sched; + unsigned long flags; + + if (arfcn > 0) { + hando_arfcn = arfcn; + hando_access_flag = 1; + printf("\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nMTZ: CP1\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n"); + uint32_t new_fn; + if (l1s.new_dm2) { + int ii = 0; + for (ii =0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == arfcn) { + printf("\n\n\n\n----------------------------------------------------------\n\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!MTZ: SYNCHING TO NEW ARFCN %d (offset=%d, freq_diff=%d, sb_fb_freq_diff=%d, sb_snr=%d, frame_diff=%d)!!!!!!!!!\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n----------------------------------------------------------\n\n\n\n\n\n", arfcn, l1s.tpu_offsets[ii], l1s.nb_freq_diff[ii], l1s.nb_sb_freq_diff[ii], l1s.nb_sb_snr[ii], l1s.nb_frame_diff[ii]); + l1s.orig_tpu_offset = l1s.tpu_offset; + l1s.tpu_offset = l1s.tpu_offsets[ii]; + afc_correct(l1s.nb_freq_diff[ii], arfcn); + + new_fn = l1s.current_time.fn + l1s.nb_frame_diff[ii]; + if (new_fn < 0) + new_fn += 2715647; + new_fn = new_fn % 2715647; + gsm_fn2gsmtime(&l1s.current_time, new_fn); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + tdma_sched_reset(); + + ho_flag = 1; + + //if (l1s.nb_sb_snr[ii] > 0) + // afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 1); + //else + // afc_input(l1s.nb_sb_freq_diff[ii], arfcn, 0); + + //mframe_disable(MF_TASK_TCH_F_EVEN); + //mframe_disable(MF_TASK_TCH_F_ODD); + //mframe_disable(MF_TASK_TCH_H_0); + //mframe_disable(MF_TASK_TCH_H_1); + + //mframe_disable(MF_TASK_NEIGH_PM51_C0T0); + //mframe_disable(MF_TASK_NEIGH_PM51); + //mframe_disable(MF_TASK_NEIGH_PM26E); + //mframe_disable(MF_TASK_NEIGH_PM26O); + + //mframe_enable(MF_TASK_BCCH_NORM); + + } + } + l1s.new_dm2 = 0; + } + } + + offset += 3; + + //printf("\n\nMTZ: In l1a_rach_req, serving cell = %d\n\n", l1s.serving_cell.arfcn); + + local_firq_save(flags); + if (l1s.dedicated.type == GSM_DCHAN_TCH_F) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame TCH/F channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + } else if (l1s.dedicated.type == GSM_DCHAN_TCH_H) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame of TCH/H channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + if ((l1s.dedicated.chan_nr & 1) != ((fn_sched % 13) & 1)) + fn_sched++; + } else if (combined) { + /* add elapsed RACH slots to offset */ + offset += t3_to_rach_comb[l1s.current_time.t3]; + /* offset is the number of RACH slots in the future */ + fn_sched = l1s.current_time.fn - l1s.current_time.t3; + fn_sched += offset / 27 * 51; + fn_sched += rach_to_t3_comb[offset % 27]; + } else + fn_sched = l1s.current_time.fn + offset; + l1s.rach.ra = ra; + fn_sched %= 2715648; + sched_gsmtime(rach_sched_set_ul, fn_sched, 0); + local_irq_restore(flags); + + memset(&last_rach, 0, sizeof(last_rach)); +} + +static __attribute__ ((constructor)) void prim_rach_init(void) +{ + l1s.completion[L1_COMPL_RACH] = &l1a_rach_compl; +} diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c index 38c7b53b..cb77d3bc 100644 --- a/src/target/firmware/layer1/prim_rx_nb.c +++ b/src/target/firmware/layer1/prim_rx_nb.c @@ -89,6 +89,9 @@ static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3) gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 1); rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn); + //As causing held issues + //printf("\nMTZ:l1s_nb_resp, sc=%d\n", l1s.serving_cell.arfcn); + /* collect measurements */ rxnb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; rxnb.meas[burst_id].pm_dbm8 = @@ -204,6 +207,7 @@ static int l1s_nb_cmd(__unused uint8_t p1, uint8_t burst_id, burst_id, tsc ); + //printf("\nMTZ: arfcn in l1s_nb_cmd = %d\n", arfcn); l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); return 0; diff --git a/src/target/firmware/layer1/prim_tch.c b/src/target/firmware/layer1/prim_tch.c index a8036d2f..808c2d3b 100644 --- a/src/target/firmware/layer1/prim_tch.c +++ b/src/target/firmware/layer1/prim_tch.c @@ -56,6 +56,9 @@ #include +/* FIXME: count of what? use better name */ +static int count11 = 0; + static inline int msb_get_bit(uint8_t *buf, int bn) { int pos_byte = bn >> 3; @@ -213,6 +216,13 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) meas_id = (meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */ + count11++; + if (count11 == 20){ + count11=0; + //printf("\nMTZ: arfcn in l1s_tch_resp = %d, sc = %d\n", arfcn, l1s.serving_cell.arfcn); + } + //printf("\nMTZ: arfcn in l1s_tch_resp = %d, sc = %d\n", arfcn, l1s.serving_cell.arfcn); + /* Collect measurements */ rx_tch.meas[meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; rx_tch.meas[meas_id].pm_dbm8 = @@ -258,6 +268,9 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) /* Allocate msgb */ /* FIXME: we actually want all allocation out of L1S! */ msg = l1ctl_msgb_alloc(L1CTL_DATA_IND); + //MTZ + //printf("\n\n\n---------------------------------\nFACCH\n---------------------------------\n\n\n"); + //printf("\n\n\n---------------------------------\nFACCH\n---------------------------------\n\n\n"); if(!msg) { printf("TCH FACCH: unable to allocate msgb\n"); goto skip_rx_facch; @@ -272,6 +285,8 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) dl->band_arfcn = htons(arfcn); dl->frame_nr = htonl(rx_time.fn); + printf("\n\nMTZ: FACCH received - chan_nr=%x\n\n", chan_nr); + /* Average SNR & RX level */ n = tch_f_hn ? 8 : 6; for (i=0; il3h; + if (msg) { //MTZ + //printf("\n\n\n---------------------------------\nFACCH\n---------------------------------\n\n\n"); + //printf("\n\n\n---------------------------------\nFACCH\n---------------------------------\n\n\n"); + data = msg->l3h;} else if (tch_mode == SIG_ONLY_MODE) data = pu_get_idle_frame(); else @@ -533,8 +550,16 @@ skip_tx_traffic: dsp_task_iq_swap(TCHT_DSP_TASK, arfcn, 0), 0, tsc /* burst_id unused for TCH */ ); + + //printf("\nMTZ: arfcn in l1s_tch_cmd = %d, sc = %d\n", arfcn, l1s.serving_cell.arfcn); + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + /* stop here, if TX is disabled */ + if (l1s.dedicated.rx_only) + return 0; + + dsp_load_tx_task( dsp_task_iq_swap(TCHT_DSP_TASK, arfcn, 1), 0, tsc /* burst_id unused for TCH */ @@ -798,8 +823,14 @@ static int l1s_tch_a_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) dsp_task_iq_swap(TCHA_DSP_TASK, arfcn, 0), 0, tsc /* burst_id unused for TCHA */ ); + //printf("\nMTZ: arfcn in l1s_tch_a_cmd = %d, sc=%d\n", arfcn, l1s.serving_cell.arfcn); l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + /* stop here, if TX is disabled */ + if (l1s.dedicated.rx_only) + return 0; + + dsp_load_tx_task( dsp_task_iq_swap(TCHA_DSP_TASK, arfcn, 1), 0, tsc /* burst_id unused for TCHA */ diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c index 86e8224c..2c27be6c 100644 --- a/src/target/firmware/layer1/prim_tx_nb.c +++ b/src/target/firmware/layer1/prim_tx_nb.c @@ -125,6 +125,7 @@ static int l1s_tx_cmd(uint8_t p1, uint8_t burst_id, uint16_t p3) burst_id, tsc ); + //printf("\nMTZ: arfcn in l1s_tx_cmd = %d, sc=%d\n", arfcn, l1s.serving_cell.arfcn); l1s_tx_win_ctrl(arfcn | ARFCN_UPLINK, L1_TXWIN_NB, 0, 3); return 0; diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c index 36f42975..6c39f51e 100644 --- a/src/target/firmware/layer1/sync.c +++ b/src/target/firmware/layer1/sync.c @@ -187,6 +187,8 @@ void l1s_reset_hw(void) static int last_timestamp; +static int last_ts; + static inline void check_lost_frame(void) { int diff, timestamp = hwtimer_read(1); @@ -195,6 +197,26 @@ static inline void check_lost_frame(void) last_timestamp += (4*TIMER_TICKS_PER_TDMA); diff = last_timestamp - timestamp; + + /* TS change compensation */ + if (l1s.dedicated.type) { + if (l1s.dedicated.tn < last_ts) { + int ediff = ((8 - last_ts + l1s.dedicated.tn) * TIMER_TICKS_PER_TDMA) >> 3; + printf("TS Chg back: %d -> %d | %d %d\n", + last_ts, l1s.dedicated.tn, diff, ediff); + + // if (((ediff - 2) < diff) && ((ediff + 2) > diff)) { + puts("ADV !\n"); + l1s.current_time = l1s.next_time; + l1s_time_inc(&l1s.next_time, 1); + // } + } else if (l1s.dedicated.tn > last_ts) + printf("TS Chg forth: %d -> %d | %d\n", + last_ts, l1s.dedicated.tn, diff); + last_ts = l1s.dedicated.tn; + } +// } else +// last_ts = 0; /* allow for a bit of jitter */ if (diff < TIMER_TICKS_PER_TDMA - TIMER_TICK_JITTER || @@ -367,6 +389,7 @@ void l1s_reset(void) /* Leave dedicated mode */ l1s.dedicated.type = GSM_DCHAN_NONE; + last_ts = 0; /* reset scheduler and hardware */ sched_gsmtime_reset(); @@ -382,10 +405,20 @@ void l1s_init(void) { unsigned int i; + for (i = 0; i < 64; i++) { + l1s.tpu_offsets_arfcn[i] = 0; + l1s.tpu_offsets[i] = 0; + l1s.nb_freq_diff[i] = 0; + l1s.nb_sb_freq_diff[i] = 0; + l1s.nb_sb_snr[i] = 0; + l1s.nb_frame_diff[i] = 0; + } + for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++) INIT_LLIST_HEAD(&l1s.tx_queue[i]); l1s.tx_meas = NULL; + sched_gsmtime_init(); /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */ diff --git a/src/target/firmware/layer1/tmpfbsb.c b/src/target/firmware/layer1/tmpfbsb.c new file mode 100644 index 00000000..f7656139 --- /dev/null +++ b/src/target/firmware/layer1/tmpfbsb.c @@ -0,0 +1,1017 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +// MTZ - for working of these variables see comments in l1s_neigh_fbsb_cmd +// MTZ - det_serving_cell overrides neigh_for_fbsb_det +int synchronize_yes = 0; //MTZ - A test variable +int det_serving_cell = 0; //MTZ - A test variable +int neigh_for_fbsb_det = 0; //MTZ - A test variable + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + //if (tpu_instr_test) { //MTZ - uncomment + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + //} +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\nMTZ: In neigh_fbsb_cmd, l1s.neigh_pm.n = %d\n", l1s.neigh_pm.n); + + //---------------------------------------------------------------------------------------- + // + // Important points of note: + // ------------------------- + // + // Temporary variables used for testing: + // + // Right now we have three global variables defined at the beginning of this file + // for testing purposes: det_serving_cell, synchronize_yes and neigh_for_fbsb_det. + // + // det_serving_cell allows for detection of FBSB on serving cell, something which we + // thought should be simpler as the clock is already synched on that cell. + // + // synchronize_yes is a variable with which one can control whether offset and synch + // commands will be sent to the TPU. There was a thought that perhaps the DSP SB + // detection task for dedicated mode might not require synching due to which this + // test variable was created. Also, naturally, detection on the serving cell should + // not require synching but we could be wrong. + // + // neigh_for_fbsb_det is a variable to hardcode the neighbour one wants to detect + // FBSB on. Note that because more functionality is added to the code the mechanism + // for traversing through neighbours using select_neigh_cell and certain flags is + // disturbed for now. Due to this the index is hardcoded in the code below to + // whichever neighbour one wants to detect FBSB on (ordered by signal strength) + // using this variable. + // + // Progress so far: + // + // Right now we are able to detect FB in dedicated mode even for neighbours. One + // additional thing we have done to aid us is that we have added 3 more frames to + // our idle frame by taking up some TCH channels (this can be seen in mframe_sched). + // The call doesn't get dropped if synchronization is not performed except for very + // few cases which we need not worry about for now. However, when synchronization + // is performed the call gets dropped without exception. + // + // Few points where the problem could lie: + // + // 1) Perhaps we are not using the TCH_SB_DSP_TASK correctly. Perhaps some other + // locations in the API also need to be written to when issuing the command in + // addition to dsp_api.db_w->d_task_md. Perhaps we are not reading from the correct + // location when checking the response. + // + // 2) Perhaps we need to start the SB detection task some quarter-bits or timeslots + // before the actual SB appears. + // + // 3) Timing issues. This is the most likely cause as we haven't really been able + // to fully understand and implement timing (if it is required, which it seems like + // it is) in the code. If timing is required then this code is quite superficial. + // In idle mode functions such as afc_correct and procedures requiring frequency + // difference are used to before calling SB detection task which are not done here. + // + // Timing issues seems to be the most likely problem. Anyone wishing to solve the + // SB detection issue should try to focus on this problem, understand how + // synchronization is performed in idle mode and how we can do that in dedicated + // mode and be back within 4 frames as after that the traffic needs to resume. + // + //---------------------------------------------------------------------------------------- + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + //MTZ - This index variable is used to hardcode the neighbour cell for now + index = neigh_for_fbsb_det; + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + + if (det_serving_cell) + printf("detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); + else + printf("detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if (sb_det == 2) { + + if (det_serving_cell) + printf("detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); + else + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + //MTZ - This is a variable for the testing phase whether to send sync commands or not + if (synchronize_yes) + synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + if (sb_det == 0) { + if (!dsp_api.ndb->d_fb_det) { + printf("\nMTZ: FB found = 0\n\n"); + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + nb_fb_angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\nMTZ: FB found = 1, FB%u, nb_fb_toa = %d\n\n", dsp_api.ndb->d_fb_mode, nb_fb_toa); + sb_det = 1; + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + + if (sb_det == 2) { + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/tmpfbsbWorking.c b/src/target/firmware/layer1/tmpfbsbWorking.c new file mode 100644 index 00000000..0baf2e9d --- /dev/null +++ b/src/target/firmware/layer1/tmpfbsbWorking.c @@ -0,0 +1,937 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + //if (tpu_instr_test) { //MTZ - uncomment + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + //} +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_fb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + index = 0; //MTZ - hardcoded - the above logic needs to be modified + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + + //printf("detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); //MTZ - Uncomment + printf("detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + //rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); //MTZ- Uncomment + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); //MTZ - Uncomment + + /* restore last gain */ + rffe_set_gain(last_gain); + } else if (sb_det ==2) { + + //printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); //Uncomment + printf("detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); + + synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + //rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); //MTZ - Uncomment + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + //MTZ - Experimenting + dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; + dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; + dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; + dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + if (sb_det == 0) { + if (!dsp_api.ndb->d_fb_det) { + printf("\nMTZ: FB found = 0\n\n"); + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + nb_fb_angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\nMTZ: FB found = 1, FB%u, nb_fb_toa = %d\n\n", dsp_api.ndb->d_fb_mode, nb_fb_toa); + sb_det = 1; + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + + if (sb_det == 2) { + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c index f4e76c16..21a3326e 100644 --- a/src/target/firmware/layer1/tpu_window.c +++ b/src/target/firmware/layer1/tpu_window.c @@ -47,7 +47,9 @@ #define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q) #define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q) #define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */ -#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +//#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +#define L1_FB26_DURATION_Q (2*L1_TDMA_LENGTH_Q + 798) +#define L1_SB26_DURATION_Q (L1_TDMA_LENGTH_Q + 984) // 984 #define L1_PW_DURATION_Q 289 #define DSP_SETUP_TIME 66 @@ -57,6 +59,8 @@ static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = { [L1_RXWIN_FB] = L1_FB_DURATION_Q, [L1_RXWIN_SB] = L1_SB_DURATION_Q, [L1_RXWIN_NB] = L1_NB_DURATION_Q, + [L1_RXWIN_FB26] = L1_FB26_DURATION_Q, + [L1_RXWIN_SB26] = L1_SB26_DURATION_Q, }; #define L1_TX_NB_DURATION_Q 626 @@ -129,6 +133,15 @@ void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn_ofs) stop -= 11 * L1_TDMA_LENGTH_Q; } + /* Delay 11 full TDMA frames */ + if (wtype == L1_RXWIN_FB26) { + uint8_t i; + for (i = 0; i < 2; i++) + tpu_enq_at(0); + + stop -= 2 * L1_TDMA_LENGTH_Q; + } + /* Window close for ABB */ twl3025_downlink(0, stop & 0xffff); diff --git a/src/target/firmware/prim_fbsbwithFB1.c b/src/target/firmware/prim_fbsbwithFB1.c new file mode 100644 index 00000000..c7a17b7f --- /dev/null +++ b/src/target/firmware/prim_fbsbwithFB1.c @@ -0,0 +1,1243 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +static int fb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added +int total_sb_det = 0; + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle0 = 0; //MTZ - This was added +uint16_t nb_fb_angle1 = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +// MTZ - for working of these variables see comments in l1s_neigh_fbsb_cmd +// MTZ - det_serving_cell overrides neigh_for_fbsb_det +int synchronize_yes = 0; //MTZ - A test variable +int det_serving_cell = 0; //MTZ - A test variable +int neigh_for_fbsb_det = 0; //MTZ - A test variable + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + printf("\nMTZ: l1s.serving_cell.ccch_mode = %d\n", l1s.serving_cell.ccch_mode); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_sbdet_cmd = %d\n", rf_arfcn); + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + printf("\nMTZ: arfcn in l1s_fbdet_cmd = %d\n", fbs.req.band_arfcn); + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test1(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + printf("\n\nMTZ - sync_test1, old_tpu_offset = %d\n\n", l1s.tpu_offset); + + old_tpu_offset = l1s.tpu_offset; + + //l1s.tpu_offset = 3000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + //tpu_enq_at(0); + //tpu_enq_at(0); + //l1s.tpu_offset = old_tpu_offset; + //l1s.tpu_offset = 2000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + + //tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(3000); + //tpu_enq_at(0); + //tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + //tpu_enq_at(0); + + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(2000); + ////tpu_enq_at(0); + ////tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + + printf("\n\nMTZ - sync_test1, ending tpu_offset = %d\n\n", l1s.tpu_offset); + + +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test2(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + printf("\n\nMTZ - sync_test2\n\n"); + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_sync(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + if (fb_det == 1) { + afc_correct(ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + } + + if ((sb_det == 2)||(sb_det == 4)) { + //printf("\nMTZ - in l1s_neigh_fbsb_sync, old_tpu_offset = %d\n", l1s.tpu_offset); + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; //MTZ - uncomment + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; //MTZ - uncomment + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + //printf("MTZ - old_tpu_offset = %d, tpu_shift = %d, qbits = %d\n", old_tpu_offset, tpu_shift, qbits); + + l1s.neigh_pm.tpu_offset[l1s.neigh_sb.index] = tpu_shift; + + int ii =0; + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]) { + l1s.tpu_offsets[ii] = tpu_shift; + break; + } + if (l1s.tpu_offsets_arfcn[ii] == 0) { + l1s.tpu_offsets_arfcn[ii] = l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]; + l1s.tpu_offsets[ii] = tpu_shift; + break; + } + } + + printf("\n\nMTZ: Stored TPU Offsets:"); + for (ii=0; ii<64; ii++) { + if (l1s.tpu_offsets_arfcn[ii] == 0) + break; + printf(" %d(%d)", l1s.tpu_offsets[ii], l1s.tpu_offsets_arfcn[ii]); + } + printf("\n\n"); + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50) { + l1s.tpu_offset = tpu_shift; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(tpu_shift); + } + afc_correct(ANGLE_TO_FREQ(nb_fb_angle1), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + +} +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + //printf("\nMTZ: In neigh_fbsb_cmd, l1s.neigh_pm.n = %d", l1s.neigh_pm.n); + + //---------------------------------------------------------------------------------------- + // + // Important points of note: + // ------------------------- + // + // Temporary variables used for testing: + // + // Right now we have three global variables defined at the beginning of this file + // for testing purposes: det_serving_cell, synchronize_yes and neigh_for_fbsb_det. + // + // det_serving_cell allows for detection of FBSB on serving cell, something which we + // thought should be simpler as the clock is already synched on that cell. + // + // synchronize_yes is a variable with which one can control whether offset and synch + // commands will be sent to the TPU. There was a thought that perhaps the DSP SB + // detection task for dedicated mode might not require synching due to which this + // test variable was created. Also, naturally, detection on the serving cell should + // not require synching but we could be wrong. + // + // neigh_for_fbsb_det is a variable to hardcode the neighbour one wants to detect + // FBSB on. Note that because more functionality is added to the code the mechanism + // for traversing through neighbours using select_neigh_cell and certain flags is + // disturbed for now. Due to this the index is hardcoded in the code below to + // whichever neighbour one wants to detect FBSB on (ordered by signal strength) + // using this variable. + // + // Progress so far: + // + // Right now we are able to detect FB in dedicated mode even for neighbours. One + // additional thing we have done to aid us is that we have added 3 more frames to + // our idle frame by taking up some TCH channels (this can be seen in mframe_sched). + // The call doesn't get dropped if synchronization is not performed except for very + // few cases which we need not worry about for now. However, when synchronization + // is performed the call gets dropped without exception. + // + // Few points where the problem could lie: + // + // 1) Perhaps we are not using the TCH_SB_DSP_TASK correctly. Perhaps some other + // locations in the API also need to be written to when issuing the command in + // addition to dsp_api.db_w->d_task_md. Perhaps we are not reading from the correct + // location when checking the response. + // + // 2) Perhaps we need to start the SB detection task some quarter-bits or timeslots + // before the actual SB appears. + // + // 3) Timing issues. This is the most likely cause as we haven't really been able + // to fully understand and implement timing (if it is required, which it seems like + // it is) in the code. If timing is required then this code is quite superficial. + // In idle mode functions such as afc_correct and procedures requiring frequency + // difference are used to before calling SB detection task which are not done here. + // + // Timing issues seems to be the most likely problem. Anyone wishing to solve the + // SB detection issue should try to focus on this problem, understand how + // synchronization is performed in idle mode and how we can do that in dedicated + // mode and be back within 4 frames as after that the traffic needs to resume. + // + //---------------------------------------------------------------------------------------- + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + +// //MTZ - putting this for now as we wanted to repeatedly detect the remaining ones - remove +// while (!((l1s.neigh_sb.flags_bsic[index] & NEIGH_PM_FLAG_BSIC) == 0)) { +//// printf("\nMTZ: BSIC has been decoded for ARFCN %d (flags_bsic[%d] = %d)\n\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.flags_bsic[index]); +// l1s.neigh_sb.count = 0; +// l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; +// select_neigh_cell(); +// index = l1s.neigh_sb.index; +// } + + //MTZ - This index variable is used to hardcode the neighbour cell for now + //index = neigh_for_fbsb_det; + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + +// if (det_serving_cell) +// printf(" - detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + if (fb_det == 1) { + dsp_api.ndb->d_fb_mode = 1; + } else { + dsp_api.ndb->d_fb_mode = 0; + } + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (FB%d) = %d\n", fb_det, l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if ((sb_det == 2)||(sb_det == 4)) { + +// if (det_serving_cell) +// printf(" - detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + //MTZ - This is a variable for the testing phase whether to send sync commands or not + //if (synchronize_yes) + // synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works + printf("\nMTZ: arfcn in l1s_neigh_fbsb_cmd (SB) = %d\n", l1s.neigh_pm.band_arfcn[index]); + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + uint8_t bsic; + int sb_found = 0; + + if (sb_det == 0) { + if (!dsp_api.ndb->d_fb_det) { + printf("MTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 0\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det); + + /* next sync */ + if (++l1s.neigh_sb.count == 11) { + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + if (fb_det == 1){ + afc_correct(-1*ANGLE_TO_FREQ(nb_fb_angle0), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + fb_det = 0; + } + } + + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + if (fb_det == 1) + nb_fb_angle1 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + else + nb_fb_angle0 = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("\n\nMTZ: ARFCN %d (index %d, power %d dbm, try #%d) FB%d found = 1 >>> nb_fb_toa = %d\n\n", l1s.neigh_pm.band_arfcn[index], index, rxlev2dbm(l1s.neigh_pm.level[index]), l1s.neigh_sb.count, fb_det, nb_fb_toa); + if (fb_det == 0) { + fb_det = 1; + } else { + sb_det = 1; + fb_det = 0; + } + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + + if ((sb_det == 2)||(sb_det == 4)) { + /* check if sync was successful */ + //if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + bsic = (sb >> 2) & 0x3f; + total_sb_det++; + //printf("=> SB 0x%08"PRIx32": BSIC=%u \n\n", sb, bsic); + printf("\n----------------------------------------------------------------------------\nSB found = 1 (ARFCN %d, power %d dbm) => SB 0x%08"PRIx32": BSIC=%u, TOA=%d (Total=%d)\n----------------------------------------------------------------------------\n\n", l1s.neigh_pm.band_arfcn[index], rxlev2dbm(l1s.neigh_pm.level[index]), sb, bsic, dsp_api.db_r->a_serv_demod[D_TOA], total_sb_det); + l1s.neigh_sb.flags_bsic[index] = bsic | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + if ((l1s.new_dm == 1) && (l1s.neigh_pm.band_arfcn[index] != 58)) { + l1s.ho_arfcn = l1s.neigh_pm.band_arfcn[index]; + l1s.new_dm = 0; + } + } + + if ((sb_det == 2)||(sb_det == 4)) { + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50); + l1s.tpu_offset = old_tpu_offset; + afc_correct(-1*(ANGLE_TO_FREQ(nb_fb_angle0) + ANGLE_TO_FREQ(nb_fb_angle1)), l1s.neigh_pm.band_arfcn[l1s.neigh_sb.index]); + + if (synchronize_yes) { + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + + if ((sb_det == 4)||(sb_found == 1)) { + l1s.neigh_sb.count = 0; + //MTZ - need to change this statement based on detection + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + if (sb_found == 0) + printf("\n\n"); + } + + } + + if ((sb_det == 4)||(sb_found == 1)) + sb_det = 0; + else + sb_det++; + } + + return 0; +} + +static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_sb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + printf("\nMTZ: In l1s_neigh_sb_resp, l1s.serving_cell.arfcn = %d\n", l1s.serving_cell.arfcn); + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_sync, 1, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(sync_test1, 1, 0, 1), SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_ITEM_DT(sync_test2, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/prim_fbsbwithTotal.c b/src/target/firmware/prim_fbsbwithTotal.c new file mode 100644 index 00000000..b325da47 --- /dev/null +++ b/src/target/firmware/prim_fbsbwithTotal.c @@ -0,0 +1,1188 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; +static int sb_det = 0; //MTZ - This was added +uint32_t old_tpu_offset = 0; //MTZ - This was added +int total_sb_det = 0; + +int16_t nb_fb_toa = 0; //MTZ - This was added +uint16_t nb_fb_pm = 0; //MTZ - This was added +uint16_t nb_fb_angle = 0; //MTZ - This was added +uint16_t nb_fb_snr = 0; //MTZ - This was added + +// MTZ - for working of these variables see comments in l1s_neigh_fbsb_cmd +// MTZ - det_serving_cell overrides neigh_for_fbsb_det +int synchronize_yes = 0; //MTZ - A test variable +int det_serving_cell = 0; //MTZ - A test variable +int neigh_for_fbsb_det = 0; //MTZ - A test variable + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%"PRIu32":%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08"PRIx32": BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%"PRIu32" + attempt=%u + ntdma = %d)\n", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%"PRIu32" - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + l1ctl_fbsb_resp(255); + return; + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + fbs.req.rxlev_exp = req->rxlev_exp; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + /* Reset the TOA loop counters */ + toa_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +/* SB for Neighbours in dedicated mode + * + * Only when number of neighbor cells is > 0, perform synchronization. + * + * For each synchronization, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending sync result would + * be ignored. + * + * After a (new) list of neighbor cells are received, the measurements are not + * yet valid. A valid state flag is used to indicate valid measurements. Until + * there are no valid measurements, the synchronization is not performed. + * + * The task is to scan the 6 strongest neighbor cells by trying to synchronize + * to it. This is done by selecting the strongest unscanned neighbor cell. + * If 6 cells have been scanned or all cells (if less than 6) have been + * scanned, the process clears all 'scanned' flags and starts over with the + * strongest (now the strongest unscanned) cell. + * + * Each synchronization attempt is performed during the "search frame" (IDLE + * frame). The process attempts to sync 11 times to ensure that it hits the + * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two + * 26-multiframe in a way that the "search frame" is aligned with the SCH, at + * least once for 11 successive "search frames".) + * + * If the synchronization attempt is successful, the BSIC and neighbor cell + * offset is stored. These are indicated to layer23 with the measurement + * results. + * + * When performing handover to a neighbor cell, the stored offset is used to + * calculate new GSM time and tpu_offset. + */ + +static void select_neigh_cell(void) +{ + uint8_t strongest = 0, strongest_unscanned = 0; + int strongest_i = 0, strongest_unscanned_i = -1; + int num_scanned = 0; + int i; + + /* find strongest cell and strongest unscanned cell and count */ + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (l1s.neigh_pm.level[i] > strongest) { + strongest = l1s.neigh_pm.level[i]; + strongest_i = i; + } + if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) { + if (l1s.neigh_pm.level[i] > strongest_unscanned) { + strongest_unscanned = l1s.neigh_pm.level[i]; + strongest_unscanned_i = i; + } + } else + num_scanned++; + } + + /* no unscanned cell or we have scanned enough */ + if (strongest_unscanned_i < 0 || num_scanned >= 6) { + /* flag all cells unscanned */ + for (i = 0; i < l1s.neigh_pm.n; i++) + l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED; + /* use strongest cell to begin scanning with */ + l1s.neigh_sb.index = strongest_i; + } else { + /* use strongest unscanned cell to begin scanning with */ + l1s.neigh_sb.index = strongest_unscanned_i; + } +} + +//MTZ - The function below has been taken from synchronize_tdma in sync.c and modified for whatever seemed necessary +void synchronize_tdma2() +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test1(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + printf("\n\nMTZ - sync_test1, old_tpu_offset = %d\n\n", l1s.tpu_offset); + + old_tpu_offset = l1s.tpu_offset; + + //l1s.tpu_offset = 3000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + //tpu_enq_at(0); + //tpu_enq_at(0); + //l1s.tpu_offset = old_tpu_offset; + //l1s.tpu_offset = 2000; + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(l1s.tpu_offset); + + //tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(3000); + //tpu_enq_at(0); + //tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + //tpu_enq_at(0); + + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(2000); + ////tpu_enq_at(0); + ////tpu_enq_at(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(0); + //tpu_enq_at(SWITCH_TIME); + //tpu_enq_sync(old_tpu_offset); + + printf("\n\nMTZ - sync_test1, ending tpu_offset = %d\n\n", l1s.tpu_offset); + + +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int sync_test2(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + printf("\n\nMTZ - sync_test2\n\n"); + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_sync(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + uint32_t tpu_shift; + int ntdma, qbits; + + if ((sb_det == 2)||(sb_det == 4)) { + //printf("\nMTZ - in l1s_neigh_fbsb_sync, old_tpu_offset = %d\n", l1s.tpu_offset); + + /* FIXME: where did this magic 23 come from? */ + nb_fb_toa -= 23; //MTZ - uncomment + + if (nb_fb_toa < 0) { + qbits = (nb_fb_toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (nb_fb_toa) / BITS_PER_TDMA; + qbits = (nb_fb_toa - ntdma * BITS_PER_TDMA) * 4; + } + + tpu_shift = qbits; + + old_tpu_offset = l1s.tpu_offset; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; //MTZ - uncomment + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + //fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + //if (tpu_shift < SWITCH_TIME) + // fn_offset++; + + //printf("MTZ - old_tpu_offset = %d, tpu_shift = %d, qbits = %d\n", old_tpu_offset, tpu_shift, qbits); + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50) { + //qbits = (nb_fb_toa%BITS_PER_TDMA) * 4; + //tpu_enq_sync((old_tpu_offset + qbits)%5000); + l1s.tpu_offset = tpu_shift; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(tpu_shift); + //tpu_enq_at(0); + } + + if (synchronize_yes) { + l1s.tpu_offset = tpu_shift; + //puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + +} +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + //printf("\nMTZ: In neigh_fbsb_cmd, l1s.neigh_pm.n = %d", l1s.neigh_pm.n); + + //---------------------------------------------------------------------------------------- + // + // Important points of note: + // ------------------------- + // + // Temporary variables used for testing: + // + // Right now we have three global variables defined at the beginning of this file + // for testing purposes: det_serving_cell, synchronize_yes and neigh_for_fbsb_det. + // + // det_serving_cell allows for detection of FBSB on serving cell, something which we + // thought should be simpler as the clock is already synched on that cell. + // + // synchronize_yes is a variable with which one can control whether offset and synch + // commands will be sent to the TPU. There was a thought that perhaps the DSP SB + // detection task for dedicated mode might not require synching due to which this + // test variable was created. Also, naturally, detection on the serving cell should + // not require synching but we could be wrong. + // + // neigh_for_fbsb_det is a variable to hardcode the neighbour one wants to detect + // FBSB on. Note that because more functionality is added to the code the mechanism + // for traversing through neighbours using select_neigh_cell and certain flags is + // disturbed for now. Due to this the index is hardcoded in the code below to + // whichever neighbour one wants to detect FBSB on (ordered by signal strength) + // using this variable. + // + // Progress so far: + // + // Right now we are able to detect FB in dedicated mode even for neighbours. One + // additional thing we have done to aid us is that we have added 3 more frames to + // our idle frame by taking up some TCH channels (this can be seen in mframe_sched). + // The call doesn't get dropped if synchronization is not performed except for very + // few cases which we need not worry about for now. However, when synchronization + // is performed the call gets dropped without exception. + // + // Few points where the problem could lie: + // + // 1) Perhaps we are not using the TCH_SB_DSP_TASK correctly. Perhaps some other + // locations in the API also need to be written to when issuing the command in + // addition to dsp_api.db_w->d_task_md. Perhaps we are not reading from the correct + // location when checking the response. + // + // 2) Perhaps we need to start the SB detection task some quarter-bits or timeslots + // before the actual SB appears. + // + // 3) Timing issues. This is the most likely cause as we haven't really been able + // to fully understand and implement timing (if it is required, which it seems like + // it is) in the code. If timing is required then this code is quite superficial. + // In idle mode functions such as afc_correct and procedures requiring frequency + // difference are used to before calling SB detection task which are not done here. + // + // Timing issues seems to be the most likely problem. Anyone wishing to solve the + // SB detection issue should try to focus on this problem, understand how + // synchronization is performed in idle mode and how we can do that in dedicated + // mode and be back within 4 frames as after that the traffic needs to resume. + // + //---------------------------------------------------------------------------------------- + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + + //MTZ - putting this for now as we wanted to repeatedly detect the remaining ones - remove + while (!((l1s.neigh_sb.flags_bsic[index] & NEIGH_PM_FLAG_BSIC) == 0)) { + printf("\nMTZ: BSIC has been decoded for ARFCN %d (flags_bsic[%d] = %d)\n\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.flags_bsic[index]); + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + select_neigh_cell(); + index = l1s.neigh_sb.index; + } + + //MTZ - This index variable is used to hardcode the neighbour cell for now + //index = neigh_for_fbsb_det; + + if (sb_det == 0) { + + //l1s.fb.mode = fb_mode; + +// if (det_serving_cell) +// printf(" - detect FB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect FB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_FB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 5); //MTZ - Original - don't think works + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_FB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_FB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + } else if ((sb_det == 2)||(sb_det == 4)) { + +// if (det_serving_cell) +// printf(" - detect SB arfcn %d (serving cell) (#%d) %d dbm\n", l1s.serving_cell.arfcn, l1s.neigh_sb.count, rxlev2dbm(fbs.req.rxlev_exp)); +// else +// printf(" - detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + //MTZ - This is a variable for the testing phase whether to send sync commands or not + //if (synchronize_yes) + // synchronize_tdma2(); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + if (det_serving_cell) + rffe_compute_gain(rxlev2dbm(fbs.req.rxlev_exp), CAL_DSP_TGT_BB_LVL); + else + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); //MTZ - Commented originally + dsp_api.ndb->d_fb_mode = 0; + +// //MTZ - Experimenting +// dsp_api.ndb->a_sync_demod[D_TOA] = nb_fb_toa; +// dsp_api.ndb->a_sync_demod[D_PM] = nb_fb_pm; +// dsp_api.ndb->a_sync_demod[D_ANGLE] = nb_fb_angle; +// dsp_api.ndb->a_sync_demod[D_SNR] = nb_fb_snr; + + + /* Program TPU */ + //l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); //MTZ - Original - don't think works + if (det_serving_cell) + l1s_rx_win_ctrl(l1s.serving_cell.arfcn, L1_RXWIN_SB26, 0); + else + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 0); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + } + return 0; +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_neigh_fbsb_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + uint8_t bsic; + int sb_found = 0; + + if (sb_det == 0) { + if (!dsp_api.ndb->d_fb_det) { + printf("MTZ: ARFCN %d (index %d - #%d) FB found = 0\n", l1s.neigh_pm.band_arfcn[index], index, l1s.neigh_sb.count); + + /* next sync */ + if (++l1s.neigh_sb.count == 11) { + l1s.neigh_sb.count = 0; + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + } + + } else { + nb_fb_toa = dsp_api.ndb->a_sync_demod[D_TOA]; + nb_fb_pm = dsp_api.ndb->a_sync_demod[D_PM]; + nb_fb_angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + nb_fb_snr = dsp_api.ndb->a_sync_demod[D_SNR]; + printf("MTZ: ARFCN %d (#%d) FB found = 1 >>> nb_fb_toa = %d\n\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, nb_fb_toa); + sb_det = 1; + } + + //l1s_reset_hw(); + tdma_sched_reset(); + } else { + + if ((sb_det == 2)||(sb_det == 4)) { + /* check if sync was successful */ + //if (dsp_api.db_r->a_sch[0] & (1<a_sch26[0] & (1<a_sch26[3] | dsp_api.ndb->a_sch26[4] << 16; + bsic = (sb >> 2) & 0x3f; + total_sb_det++; + //printf("=> SB 0x%08"PRIx32": BSIC=%u \n\n", sb, bsic); + printf("\n----------------------------------------------------------------\nSB found = 1 (ARFCN %d) => SB 0x%08"PRIx32": BSIC=%u (Total=%d)\n----------------------------------------------------------------\n\n", l1s.neigh_pm.band_arfcn[index], sb, bsic, total_sb_det); + l1s.neigh_sb.flags_bsic[index] = bsic | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + } + + if ((sb_det == 2)||(sb_det == 4)) { + + //MTZ - testing this - possibly remove + if (nb_fb_toa >= 50); + l1s.tpu_offset = old_tpu_offset; + + if (synchronize_yes) { + l1s.tpu_offset = old_tpu_offset; + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); + } + } + + if ((sb_det == 4)||(sb_found == 1)) { + l1s.neigh_sb.count = 0; + //MTZ - need to change this statement based on detection + l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED; + + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + if (sb_found == 0) + printf("\n\n"); + } + + } + + if ((sb_det == 4)||(sb_found == 1)) + sb_det = 0; + else + sb_det++; + } + + return 0; +} + +static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint8_t last_gain; + + printf("\n\n\nMTZ: In neigh_sb_cmd, l1s.neigh_pm.n = %d\n\n\n", l1s.neigh_pm.n); + + if (l1s.neigh_pm.n == 0) + return 0; + + /* if measurements are not yet valid, wait */ + if (!l1s.neigh_pm.valid) + return 0; + + /* check for cell to sync to */ + if (l1s.neigh_sb.count == 0) { + /* there is no cell selected, search for cell */ + select_neigh_cell(); + } + + printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index])); + + last_gain = rffe_get_gain(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */ +// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0); + dsp_api.ndb->d_fb_mode = 0; + + /* Program TPU */ + l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_sb.running = 1; + + return 0; +} + +static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + int index = l1s.neigh_sb.index; + uint32_t sb; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running) + goto out; + + /* check if sync was successful */ + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + l1s.neigh_sb.flags_bsic[index] = + l1s_decode_sb(&fbs.mon.time, sb) + | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED; + printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]); + + /* store time offset */ + } + +out: + l1s.neigh_sb.running = 0; + + dsp_api.r_page_used = 1; + + return 0; + +} + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_FRAME(), +// SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + +/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +const struct tdma_sched_item neigh_sync_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_fbsb_sync, 1, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_neigh_fbsb_cmd, 1, 0, 1), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_fbsb_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +///* NOTE: Prio 1 is below TCH's RX+TX prio 0 */ +//const struct tdma_sched_item neigh_sync_sched_set[] = { +// SCHED_ITEM_DT(sync_test1, 1, 0, 1), SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_END_FRAME(), +//// SCHED_ITEM_DT(sync_test2, 1, 0, 1), SCHED_END_FRAME(), +// SCHED_END_SET() +//}; + + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} -- cgit v1.2.3