summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/target/firmware/include/layer1/prim.h3
-rw-r--r--src/target/firmware/include/layer1/sync.h19
-rw-r--r--src/target/firmware/layer1/l23_api.c28
-rw-r--r--src/target/firmware/layer1/mframe_sched.c35
-rw-r--r--src/target/firmware/layer1/prim_pm.c177
5 files changed, 212 insertions, 50 deletions
diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h
index 30c51ae6..bff51deb 100644
--- a/src/target/firmware/include/layer1/prim.h
+++ b/src/target/firmware/include/layer1/prim.h
@@ -29,6 +29,7 @@ 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[];
#endif /* _L1_PRIM_H */
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
index 7ce87566..af743195 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -129,6 +129,7 @@ struct l1s_state {
GSM_DCHAN_UNKNOWN,
} type;
+ uint8_t chan_nr;
uint8_t scn;
uint8_t tsc;
uint8_t tn;
@@ -149,14 +150,18 @@ struct l1s_state {
};
} dedicated;
- /* neighbour cell power measurement process */
+ /* neighbor 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 */
} neigh_pm;
};
diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c
index c7849aeb..e487864e 100644
--- a/src/target/firmware/layer1/l23_api.c
+++ b/src/target/firmware/layer1/l23_api.c
@@ -69,8 +69,8 @@ void l1_queue_for_l2(struct msgb *msg)
enum mf_type {
MFNONE,
MF51,
- MF26ODD,
- MF26EVEN
+ MF26EVEN,
+ MF26ODD
};
static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr, uint8_t neigh_mode)
{
@@ -245,11 +245,12 @@ 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.chan_nr = ul->chan_nr;
+ l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr);
+ l1s.dedicated.tsc = est_req->tsc;
old_tn = l1s.dedicated.tn;
- l1s.dedicated.tn = ul->chan_nr & 0x7;
- l1s.dedicated.h = est_req->h;
+ l1s.dedicated.tn = ul->chan_nr & 0x7;
+ l1s.dedicated.h = est_req->h;
if (est_req->h) {
int i;
@@ -276,6 +277,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg)
}
/* figure out which MF tasks to enable */
+ l1s.neigh_pm.n = 0;
l1a_mftask_set(chan_nr2mf_task_mask(ul->chan_nr, NEIGH_MODE_PM));
/* shift TPU according to chnage in TN */
@@ -551,20 +553,30 @@ 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;
}
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 089ca8ed..b242f990 100644
--- a/src/target/firmware/layer1/mframe_sched.c
+++ b/src/target/firmware/layer1/mframe_sched.c
@@ -50,7 +50,8 @@ 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
/* BCCH Normal */
static const struct mframe_sched_item mf_bcch_norm[] = {
@@ -210,17 +211,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 }
};
@@ -301,13 +302,27 @@ static const struct mframe_sched_item mf_tch_h_1[] = {
{ .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 = 13, .frame_nr = 10 },
{ .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 = 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 = NULL }
};
diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c
index 5c8c4914..a4b8e638 100644
--- a/src/target/firmware/layer1/prim_pm.c
+++ b/src/target/firmware/layer1/prim_pm.c
@@ -156,11 +156,39 @@ 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;
+ }
+ 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 +216,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 +230,129 @@ 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;
+ 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);
+
+ l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] += level;
+
+
+ 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;
- /* 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];
- mi->pm[0] = l1s.neigh_pm.level[i];
- mi->pm[1] = 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;
}
- l1_queue_for_l2(msg);
}
out:
@@ -233,10 +361,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()
};