summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-04-24 00:29:44 +0200
committerHarald Welte <laforge@gnumonks.org>2017-01-15 09:17:33 +0100
commit5d11a0b0f5056dca57329b83f18b4b5fa4a6d2d6 (patch)
tree1f646bcda9901d631b74c8d56c00048bec97fdc1
parentc695ae5eb9fc0c668036d2846ba4f21865e0e040 (diff)
layer1: experimental support for neighbor cell SB detectionlaforge/neigh_sb
-rw-r--r--src/target/firmware/include/layer1/prim.h4
-rw-r--r--src/target/firmware/include/layer1/sync.h17
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c186
3 files changed, 197 insertions, 10 deletions
diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h
index 30c51ae..9571934 100644
--- a/src/target/firmware/include/layer1/prim.h
+++ b/src/target/firmware/include/layer1/prim.h
@@ -18,6 +18,8 @@ void l1s_sb_test(uint8_t base_fn);
void l1s_pm_test(uint8_t base_fn, uint16_t arfcn);
void l1s_nb_test(uint8_t base_fn);
+void l1s_neigh_sb_cfg(enum l1s_nsb_mode mode, uint8_t detect_count);
+
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);
@@ -26,6 +28,8 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra);
extern const struct tdma_sched_item nb_sched_set[];
extern const struct tdma_sched_item nb_sched_set_ul[];
+extern const struct tdma_sched_item neigh_sb_sched_set[];
+
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[];
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
index dae85a1..4ce6e5a 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -7,6 +7,8 @@
#include <layer1/mframe_sched.h>
#include <l1ctl_proto.h>
+#define L1_CINF_F_VALID 0x01
+
/* structure representing L1 sync information about a cell */
struct l1_cell_info {
/* on which ARFCN (+band) is the cell? */
@@ -22,6 +24,7 @@ struct l1_cell_info {
* with the cells burst */
uint32_t time_alignment;
/* FIXME: should we also store the AFC value? */
+ uint8_t flags;
};
enum l1s_chan {
@@ -36,6 +39,7 @@ enum l1_compl {
L1_COMPL_RACH,
L1_COMPL_TX_NB,
L1_COMPL_TX_TCH,
+ L1_COMPL_NEIGH_SB,
};
typedef void l1_compl_cb(enum l1_compl c);
@@ -54,6 +58,11 @@ struct l1s_h1 {
uint16_t ma[64];
};
+enum l1s_nsb_mode {
+ L1S_NSB_DETECT,
+ L1S_NSB_CONFIRM,
+};
+
struct l1s_state {
struct gsm_time current_time; /* current GSM time */
struct gsm_time next_time; /* GSM time at next TMDMA irq */
@@ -63,6 +72,7 @@ struct l1s_state {
/* neighbor cell sync info */
struct l1_cell_info neigh_cell[L1S_NUM_NEIGH_CELL];
+ unsigned int num_neigh_cell;
/* TDMA scheduler */
struct tdma_scheduler tdma_sched;
@@ -99,6 +109,13 @@ struct l1s_state {
} fb;
struct {
+ enum l1s_nsb_mode mode;
+ uint8_t detect_count; /* total number of attempts in detect mode */
+ uint8_t detect_count_remain; /* how many attempts remaining */
+ uint8_t neigh_idx; /* index into l1s.neigh_cell */
+ } sb;
+
+ struct {
/* power measurement l1 task */
unsigned int mode;
union {
diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c
index c0cb905..982bb50 100644
--- a/src/target/firmware/layer1/prim_fbsb.c
+++ b/src/target/firmware/layer1/prim_fbsb.c
@@ -172,8 +172,8 @@ static void read_sb_result(struct mon_state *st, int attempt)
* 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)
+static int l1s_sb2det_resp(__unused uint8_t p1, uint8_t attempt,
+ __unused uint16_t p3)
{
uint32_t sb;
int qbits, fn_offset;
@@ -241,10 +241,10 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
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
+ * are further l1s_sb2det_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
+ * l1s_sb2det_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
@@ -268,8 +268,8 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
return 0;
}
-static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
- __unused uint16_t p3)
+static int l1s_sb2det_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
{
putchart('S');
@@ -287,11 +287,11 @@ static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
/* This is how it is done by the TSM30 */
static const struct tdma_sched_item sb2_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_ITEM_DT(l1s_sb2det_cmd, 0, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM_DT(l1s_sb2det_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_ITEM(l1s_sb2det_resp, -4, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_sb2det_resp, -4, 0, 2), SCHED_END_FRAME(),
SCHED_END_SET()
};
@@ -567,7 +567,173 @@ void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req)
}
+/* SB for Neighbours in dedicated mode ****************************************/
+
+static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ struct l1_cell_info *cinfo = &l1s.neigh_cell[l1s.sb.neigh_idx];
+
+ 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 */
+
+ switch (l1s.sb.mode) {
+ case L1S_NSB_DETECT:
+ /* Program TPU using ARFCN from current cinfo */
+ l1s_rx_win_ctrl(cinfo->arfcn, L1_RXWIN_SB, 0);
+ break;
+ case L1S_NSB_CONFIRM:
+ /* FIXME: we need to load new sync values from cinfo first !!! */
+ break;
+ }
+
+ return 0;
+}
+
+/* compute which neighbor cell will do the next SB detection */
+static int l1s_neigh_proceed_next(void)
+{
+ struct l1_cell_info *cinfo = &l1s.neigh_cell[l1s.sb.neigh_idx];
+ uint8_t old_neigh_idx = l1s.sb.neigh_idx;
+ unsigned int i;
+
+ switch (l1s.sb.mode) {
+ case L1S_NSB_DETECT:
+ if (!cinfo->flags & L1_CINF_F_VALID) {
+ if (l1s.sb.detect_count_remain > 0) {
+ /* retry on same arfcn / cinfo */
+ l1s.sb.detect_count_remain--;
+ } else {
+ /* select next cell */
+ l1s.sb.neigh_idx = (l1s.sb.neigh_idx + 1)
+ % l1s.num_neigh_cell;
+ l1s.sb.detect_count_remain = l1s.sb.detect_count;
+ }
+ } else {
+ /* select next cell */
+ l1s.sb.neigh_idx = (l1s.sb.neigh_idx + 1)
+ % l1s.num_neigh_cell;
+ l1s.sb.detect_count_remain = l1s.sb.detect_count;
+ }
+ break;
+ case L1S_NSB_CONFIRM:
+ /* when we try to confirm, we proceed to the next neighbor cell that
+ * is valid, i.e. we already have synchronization values */
+ for (i = (l1s.sb.neigh_idx + 1) % l1s.num_neigh_cell;
+ i < l1s.sb.neigh_idx; i++) {
+ if (l1s.neigh_cell[i].flags & L1_CINF_F_VALID) {
+ l1s.sb.neigh_idx = i;
+ break;
+ }
+ }
+ break;
+ }
+
+ /* return 1 in case we have wrapped over the end of neighbor array */
+ if (old_neigh_idx > l1s.sb.neigh_idx)
+ return 1;
+
+ return 0;
+}
+
+static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ struct l1_cell_info *cinfo = &l1s.neigh_cell[l1s.sb.neigh_idx];
+ int need_report;
+ uint32_t sb;
+ int qbits, fn_offset;
+ int fnr_delta, bits_delta;
+
+ putchart('s');
+
+ if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) {
+ /* mark READ page as being used */
+ dsp_api.r_page_used = 1;
+
+ /* we have failed due to CRC error */
+ printf("NSB ERROR: ARFCN=%u ", cinfo->arfcn);
+ cinfo->flags &= ~L1_CINF_F_VALID;
+ goto proceed_out;
+ }
+
+ /* Success */
+ read_sb_result(last_fb, 1);
+
+ 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("NSB SUCCESS: ARFCN=%u BSIC=%u ", cinfo->arfcn, fbs.mon.bsic);
+ l1s_time_dump(&fbs.mon.time);
+
+ /* 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 - 1;
+ bits_delta = fnr_delta * BITS_PER_TDMA;
+
+ /* update neighbor cell info with the new sync information */
+ cinfo->fn_offset = fnr_delta;
+ cinfo->time_alignment = qbits;
+ cinfo->arfcn = rf_arfcn;
+ cinfo->flags |= L1_CINF_F_VALID;
+
+ 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);
+
+proceed_out:
+ /* compute next cell and tell us if we need to report results */
+ need_report = l1s_neigh_proceed_next();
+
+ /* decide if we need to report a result up the stack */
+ if (need_report)
+ l1s_compl_sched(L1_COMPL_NEIGH_SB);
+
+ return 0;
+}
+
+const struct tdma_sched_item neigh_sb_sched_set[] = {
+ SCHED_ITEM_DT(l1s_neigh_sb_cmd, 0, 0, 1), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+/* Asynchronous completion handler for FB detection */
+static void l1a_neigh_sb_compl(__unused enum l1_compl c)
+{
+ struct l1_cell_info *cinfo = &l1s.neigh_cell[l1s.sb.neigh_idx];
+
+ /* FIXME: do something with the results in the neighbor cell array */
+}
+
+/* configure the parameters for l1s neighbour SB detection/confirmation */
+void l1s_neigh_sb_cfg(enum l1s_nsb_mode mode, uint8_t detect_count)
+{
+ l1s.sb.mode = mode;
+ l1s.sb.detect_count = detect_count;
+}
+
static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void)
{
l1s.completion[L1_COMPL_FB] = &l1a_fb_compl;
+ l1s.completion[L1_COMPL_NEIGH_SB] = &l1a_neigh_sb_compl;
}