From 74e7535ea2718d810a876bbc572c7fc2f99225a1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 17 Jul 2011 09:41:19 +0200 Subject: [layer1] Adding neighbour cell measurement code to layer1. When listening to BCCH, layer1 may measure the power level of neighbour cells. A list of neighbour cell frequencies need to be sent to layer1. After the measurement is done, the results are indicated to layer23. --- src/target/firmware/layer1/l23_api.c | 76 ++++++++++++++++++++++++---- src/target/firmware/layer1/mframe_sched.c | 21 ++++++++ src/target/firmware/layer1/prim_pm.c | 84 +++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 9 deletions(-) (limited to 'src/target/firmware/layer1') diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index 2e951601..0560e449 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -54,34 +54,61 @@ void l1_queue_for_l2(struct msgb *msg) sercomm_sendmsg(SC_DLCI_L1A_L23, msg); } -static enum mframe_task chan_nr2mf_task(uint8_t chan_nr) +enum mf_type { + MFNONE, + MF51, + MF26ODD, + MF26EVEN +}; +static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr, uint8_t neigh_mode) { uint8_t cbits = chan_nr >> 3; uint8_t tn = chan_nr & 0x7; uint8_t lch_idx; + enum mframe_task master_task = 0; + uint32_t neigh_task = 0; + enum mf_type multiframe; if (cbits == 0x01) { lch_idx = 0; - return (tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN; + master_task = (tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN; + multiframe = (tn & 1) ? MF26ODD : MF26EVEN; } else if ((cbits & 0x1e) == 0x02) { lch_idx = cbits & 0x1; - return MF_TASK_TCH_H_0 + lch_idx; + master_task = MF_TASK_TCH_H_0 + lch_idx; } else if ((cbits & 0x1c) == 0x04) { lch_idx = cbits & 0x3; - return MF_TASK_SDCCH4_0 + lch_idx; + master_task = MF_TASK_SDCCH4_0 + lch_idx; + multiframe = MF51; } else if ((cbits & 0x18) == 0x08) { lch_idx = cbits & 0x7; - return MF_TASK_SDCCH8_0 + lch_idx; + master_task = MF_TASK_SDCCH8_0 + lch_idx; + multiframe = MF51; #if 0 } else if (cbits == 0x10) { /* FIXME: when to do extended BCCH? */ - return MF_TASK_BCCH_NORM; + master_task = MF_TASK_BCCH_NORM; } else if (cbits == 0x11 || cbits == 0x12) { /* FIXME: how to decide CCCH norm/extd? */ - return MF_TASK_BCCH_CCCH; + master_task = MF_TASK_BCCH_CCCH; #endif } - return 0; + switch (neigh_mode) { + case NEIGH_MODE_PM: + switch (multiframe) { + case MF51: + neigh_task = (1 << MF_TASK_NEIGH_PM51); + break; + case MF26EVEN: + neigh_task = (1 << MF_TASK_NEIGH_PM26E); + break; + case MF26ODD: + neigh_task = (1 << MF_TASK_NEIGH_PM26O); + break; + } + break; + } + return (1 << master_task) | neigh_task; } static int chan_nr2dchan_type(uint8_t chan_nr) @@ -182,6 +209,9 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) 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); + /* disable neighbour cell measurement */ + mframe_disable(MF_TASK_NEIGH_PM51); + /* configure dedicated channel state */ l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); l1s.dedicated.tsc = est_req->tsc; @@ -212,7 +242,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) } /* figure out which MF tasks to enable */ - l1a_mftask_set(1 << chan_nr2mf_task(ul->chan_nr)); + l1a_mftask_set(chan_nr2mf_task_mask(ul->chan_nr, NEIGH_MODE_PM)); } /* receive a L1CTL_DM_FREQ_REQ from L23 */ @@ -277,6 +307,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(0); + l1s.neigh_pm.n = 0; } /* receive a L1CTL_PARAM_REQ from L23 */ @@ -462,6 +493,30 @@ static void l1ctl_rx_tch_mode_req(struct msgb *msg) l1ctl_tx_tch_mode_conf(tch_mode); } +/* receive a L1CTL_NEIGH_PM_REQ from L23 */ +static void l1ctl_rx_neigh_pm_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_neigh_pm_req *pm_req = + (struct l1ctl_neigh_pm_req *) l1h->data; + int i; + + /* 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.running = 0; + for (i = 0; i < pm_req->n; i++) + l1s.neigh_pm.band_arfcn[i] = ntohs(pm_req->band_arfcn[i]); + printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req->n); + l1s.neigh_pm.n = pm_req->n; /* atomic */ + + /* on BCCH enable PM on frame 51 */ + if (l1s.dedicated.type == GSM_DCHAN_NONE) + mframe_enable(MF_TASK_NEIGH_PM51); +} + /* callback from SERCOMM when L2 sends a message to L1 */ static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) { @@ -522,6 +577,9 @@ static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) case L1CTL_TCH_MODE_REQ: l1ctl_rx_tch_mode_req(msg); break; + case L1CTL_NEIGH_PM_REQ: + l1ctl_rx_neigh_pm_req(msg); + break; } exit_msgbfree: diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 428102a7..6281c3d6 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -50,6 +50,7 @@ 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 /* BCCH Normal */ static const struct mframe_sched_item mf_bcch_norm[] = { @@ -197,6 +198,12 @@ static const struct mframe_sched_item mf_sdcch8_7[] = { { .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 = NULL } +}; + /* TCH */ #define TCH tch_sched_set #define TCH_A tch_a_sched_set @@ -274,6 +281,16 @@ static const struct mframe_sched_item mf_tch_h_1[] = { { .sched_set = NULL } }; +/* Measurement for MF 26 */ +static const struct mframe_sched_item mf_neigh_pm26_even[] = { + { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 25 }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_neigh_pm26_odd[] = { + { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 12 }, + { .sched_set = NULL } +}; + /* Test TX */ static const struct mframe_sched_item mf_tx_all_nb[] = { { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 }, @@ -305,6 +322,10 @@ static const struct mframe_sched_item *sched_set_for_task[32] = { [MF_TASK_TCH_H_0] = mf_tch_h_0, [MF_TASK_TCH_H_1] = mf_tch_h_1, + [MF_TASK_NEIGH_PM51] = mf_neigh_pm51, + [MF_TASK_NEIGH_PM26E] = mf_neigh_pm26_even, + [MF_TASK_NEIGH_PM26O] = mf_neigh_pm26_odd, + [MF_TASK_UL_ALL_NB] = mf_tx_all_nb, }; diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c index e6a79553..c2d85ac7 100644 --- a/src/target/firmware/layer1/prim_pm.c +++ b/src/target/firmware/layer1/prim_pm.c @@ -152,3 +152,87 @@ void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) tdma_schedule_set(base_fn, pm_sched_set, arfcn); local_irq_restore(flags); } + +/* + * perform measurements of neighbour cells + */ + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_neigh_pm_cmd(uint8_t num_meas, + __unused uint8_t p2, __unused uint16_t p3) +{ + uint8_t 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 */ + /* FIXME: RXWIN_PW needs to set up multiple times in case + * num_meas > 1 */ + /* do measurement dummy, in case l1s.neigh_pm.n == 0 */ + l1s_rx_win_ctrl((l1s.neigh_pm.n) ? + l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] : 0, L1_RXWIN_PW, 0); + + /* 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_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[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); + } + mi = (struct l1ctl_neigh_pm_ind *) + msgb_put(msg, sizeof(*mi)); + mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]); + mi->pm[0] = l1s.neigh_pm.level[i]; + mi->pm[1] = 0; + } + l1_queue_for_l2(msg); + } + +out: + l1s.neigh_pm.running = 0; + + return 0; +} + +const struct tdma_sched_item neigh_pm_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_pm_cmd, 0, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_pm_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + -- cgit v1.2.3