/* Layer 1 Power Measurement */ /* (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. * */ #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 static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm) { uint8_t i; for (i = 0; i < nbmeas; i++) pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3); dsp_api.r_page_used = 1; } /* scheduler callback to issue a power measurement task to the DSP */ static int l1s_pm_cmd(uint8_t num_meas, __unused uint8_t p2, uint16_t arfcn) { putchart('P'); 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 */ 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 */ l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW, 0); //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB); return 0; } /* scheduler callback to read power measurement resposnse from the DSP */ static int l1s_pm_resp(uint8_t num_meas, __unused uint8_t p2, uint16_t arfcn) { struct l1ctl_pm_conf *pmr; uint16_t pm_level[2]; putchart('p'); l1ddsp_meas_read(num_meas, pm_level); printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n", arfcn, pm_level[0]/8, agc_inp_dbm8_by_pm(pm_level[0])/8); printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n", agc_inp_dbm8_by_pm(pm_level[0])/8, agc_inp_dbm8_by_pm(pm_level[1])/8, arfcn); if (!l1s.pm.msg) l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); if (msgb_tailroom(l1s.pm.msg) < sizeof(*pmr)) { /* flush current msgb */ l1_queue_for_l2(l1s.pm.msg); /* allocate a new msgb and initialize header */ l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); } pmr = (struct l1ctl_pm_conf *) msgb_put(l1s.pm.msg, sizeof(*pmr)); pmr->band_arfcn = htons(arfcn); /* FIXME: do this as RxLev rather than DBM8 ? */ pmr->pm[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[0])/8); if (num_meas > 1) pmr->pm[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[1])/8); else pmr->pm[1] = 0; if (l1s.pm.mode == 1) { if (l1s.pm.range.arfcn_next != l1s.pm.range.arfcn_end) { /* schedule PM for next ARFCN in range */ l1s.pm.range.arfcn_next = (l1s.pm.range.arfcn_next+1) & 0xfbff; l1s_pm_test(1, l1s.pm.range.arfcn_next); } else { /* we have finished, flush the msgb to L2 */ struct l1ctl_hdr *l1h; l1h = (struct l1ctl_hdr *) l1s.pm.msg->l1h; l1h->flags |= L1CTL_F_DONE; l1_queue_for_l2(l1s.pm.msg); l1s.pm.msg = NULL; } } return 0; } static const struct tdma_sched_item pm_sched_set[] = { SCHED_ITEM_DT(l1s_pm_cmd, 0, 1, 0), SCHED_END_FRAME(), SCHED_END_FRAME(), SCHED_ITEM(l1s_pm_resp, -4, 1, 0), SCHED_END_FRAME(), SCHED_END_SET() }; /* Schedule a power measurement test */ void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) { unsigned long flags; printd("l1s_pm_test(%u, %u)\n", base_fn, arfcn); local_firq_save(flags); 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, l1s.neigh_pm.tn[l1s.neigh_pm.pos]); /* 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->tn = l1s.neigh_pm.tn[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() };