summaryrefslogtreecommitdiffstats
path: root/src/target/firmware/apps/rssi/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/target/firmware/apps/rssi/main.c')
-rw-r--r--src/target/firmware/apps/rssi/main.c774
1 files changed, 774 insertions, 0 deletions
diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c
new file mode 100644
index 00000000..5d247958
--- /dev/null
+++ b/src/target/firmware/apps/rssi/main.c
@@ -0,0 +1,774 @@
+/* Cell Monitor of Free Software for Calypso Phone */
+
+/* (C) 2012 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <delay.h>
+#include <byteorder.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+#include <calypso/buzzer.h>
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+#include <fb/framebuffer.h>
+#include <layer1/sync.h>
+#include <layer1/async.h>
+#include <layer1/l23_api.h>
+
+enum key_codes key_code = KEY_INV;
+int key_pressed = 0;
+enum key_codes key_pressed_code;
+unsigned long key_pressed_when;
+unsigned int key_pressed_delay;
+
+enum mode {
+ MODE_MAIN,
+ MODE_SPECTRUM,
+ MODE_ARFCN,
+} mode = MODE_MAIN;
+enum mode last_mode; /* where to return after entering ARFCN */
+
+static uint16_t arfcn = 0;
+int pcs = 0;
+int uplink = 0;
+int max = 0;
+uint8_t power, max_power;
+char input[5];
+int cursor;
+
+static struct band {
+ int min, max, prev, next, freq_ul, freq_dl;
+} bands[] = {
+ { 128, 251, 124, 512, 8242, 8692 }, /* GSM 850 */
+ { 955, 124, 885, 128, 8762, 9212 }, /* P,E,R GSM */
+ { 512, 885, 251, 955, 17102, 18052 }, /* DCS 1800 */
+ { 0, 0, 0, 0, 0, 0},
+};
+
+struct band *band;
+
+#define PCS_MIN 512
+#define PCS_MAX 810
+#define DCS_MIN 512
+#define DCS_MAX 885
+#define PCS_UL 18502
+#define PCS_DL 19302
+
+enum pm_mode {
+ PM_IDLE,
+ PM_SENT,
+ PM_RANGE_SENT,
+ PM_RANGE_RESULT,
+ PM_RESULT,
+} pm_mode = PM_IDLE;
+
+#define NUM_PM_DL 2
+#define NUM_PM_UL 10
+int pm_meas[NUM_PM_UL];
+int pm_count = 0;
+int pm_max = 2;
+uint8_t pm_spectrum[1024];
+int pm_scale = 1; /* scale measured power level */
+
+#define TONE_JIFFIES 4
+int tone = 0;
+unsigned long tone_time;
+int tone_on = 0;
+
+/* UI */
+
+static void refresh_display(void)
+{
+ char text[16];
+
+ fb_clear();
+
+ /* header */
+ fb_setbg(FB_COLOR_WHITE);
+ if (mode != MODE_SPECTRUM) {
+ fb_setfg(FB_COLOR_BLUE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(0,6);
+ fb_putstr("Osmocom Monitor Tool",-1);
+ fb_gotoxy(0,10);
+ fb_setfg(FB_COLOR_BLACK);
+ fb_boxto(framebuffer->width-1,10);
+ }
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setfont(FB_FONT_C64);
+
+ /* ARFCN */
+ if (mode == MODE_MAIN || mode == MODE_ARFCN) {
+ fb_gotoxy(0,20);
+ if (mode == MODE_ARFCN)
+ sprintf(text, "ARFCN %s", input);
+ else if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
+ sprintf(text, "ARFCN %dPCS", arfcn);
+ else if (arfcn >= DCS_MIN && arfcn <= DCS_MAX)
+ sprintf(text, "ARFCN %dDCS", arfcn);
+ else
+ sprintf(text, "ARFCN %d", arfcn);
+ fb_putstr(text,framebuffer->width);
+ }
+
+ /* cursor */
+ if (mode == MODE_ARFCN) {
+ fb_setfg(FB_COLOR_WHITE);
+ fb_setbg(FB_COLOR_BLUE);
+ fb_putstr(" ", framebuffer->width);
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ }
+
+ /* Frequency / power */
+ if (mode == MODE_MAIN) {
+ int f;
+
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
+ if (uplink)
+ f = PCS_UL;
+ else
+ f = PCS_DL;
+ } else if (uplink)
+ f = band->freq_ul;
+ else
+ f = band->freq_dl;
+ f += ((arfcn - band->min) & 1023) << 1;
+
+ fb_gotoxy(0,30);
+ sprintf(text, "Freq. %d.%d", f / 10, f % 10);
+ fb_putstr(text,framebuffer->width);
+
+ fb_gotoxy(0,40);
+ sprintf(text, "Power %d", ((max) ? max_power : power) - 110);
+ fb_putstr(text,framebuffer->width);
+ if (max) {
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(80,39);
+ fb_putstr("max",framebuffer->width);
+ fb_setfont(FB_FONT_C64);
+ }
+ fb_setbg(FB_COLOR_BLACK);
+ fb_gotoxy(0,45);
+ fb_boxto(framebuffer->width * power / 64, 50);
+ if (max) {
+ fb_gotoxy(framebuffer->width * max_power / 64 ,45);
+ fb_boxto(framebuffer->width * max_power / 64, 50);
+ }
+ fb_setbg(FB_COLOR_WHITE);
+ }
+
+ /* spectrum */
+ if (mode == MODE_SPECTRUM) {
+ int i;
+ uint16_t a, e, p;
+
+ fb_gotoxy(0,8);
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
+ sprintf(text, "%4dP", arfcn);
+ else if (arfcn >= DCS_MIN && arfcn <= DCS_MAX)
+ sprintf(text, "%4dD", arfcn);
+ else
+ sprintf(text, "%4d ", arfcn);
+ sprintf(text + 5, " %d", pm_spectrum[arfcn & 1023] - 110);
+ fb_putstr(text,framebuffer->width);
+ if (max) {
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(80,15);
+ fb_putstr("max",framebuffer->width);
+ fb_setfont(FB_FONT_C64);
+ }
+ if (pm_scale != 1) {
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(1,15);
+ sprintf(text, "x%d", pm_scale);
+ fb_putstr(text,framebuffer->width);
+ fb_setfont(FB_FONT_C64);
+ }
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
+ a = PCS_MIN;
+ e = PCS_MAX;
+ } else {
+ a = band->min;
+ e = band->max;
+ }
+ for (i = 0; i < framebuffer->width - 1; i++) {
+ p = (arfcn + i - (framebuffer->width >> 1)) & 1023;
+ if ((((p - a) & 1023) & 512))
+ continue;
+ if ((((e - p) & 1023) & 512))
+ continue;
+ p = (pm_spectrum[p] * pm_scale * 40 / 64);
+ if (p > 40)
+ p = 40;
+ fb_gotoxy(i, 50 - p);
+ fb_boxto(i, 50);
+ }
+ i = framebuffer->width >> 1;
+ fb_gotoxy(i, 0);
+ fb_boxto(i, 4);
+ fb_gotoxy(i, 50);
+ fb_boxto(i, 54);
+ }
+
+ /* footer */
+ fb_gotoxy(0,55);
+ fb_boxto(framebuffer->width-1,55);
+ fb_gotoxy(0,64);
+ if (mode == MODE_ARFCN)
+ sprintf(text, "%s %s", (cursor) ? "del " : "back",
+ (cursor) ? "enter" : " ");
+ else
+ sprintf(text, "%s %s", (pcs) ? "PCS" : "DCS",
+ (uplink) ? "UL" : "DL");
+ fb_putstr(text,framebuffer->width);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(0,63);
+ sprintf(text, "%d", tone / 25);
+ fb_putstr(text,-1);
+
+ fb_flush();
+}
+
+static void exit_arfcn(void)
+{
+ mode = last_mode;
+ refresh_display();
+}
+
+static void enter_arfcn(enum key_codes code)
+{
+ /* enter mode */
+ if (mode != MODE_ARFCN) {
+ last_mode = mode;
+ mode = MODE_ARFCN;
+ input[0] = code - KEY_0 + '0';
+ input[1] = '\0';
+ cursor = 1;
+ refresh_display();
+ return;
+ }
+
+ if (code == KEY_LEFT_SB) {
+ /* back */
+ if (cursor == 0) {
+ exit_arfcn();
+ return;
+ }
+ /* delete */
+ cursor--;
+ input[cursor] = '\0';
+ refresh_display();
+ return;
+ }
+
+ if (code == KEY_RIGHT_SB) {
+ int check = 0;
+ int i;
+ struct band *temp = NULL;
+
+ /* nothing entered */
+ if (cursor == 0) {
+ return;
+ }
+ for (i = 0; i < cursor; i++)
+ check = (check << 3) + (check << 1) + input[i] - '0';
+
+ /* check */
+ for (i = 0; bands[i].max; i++) {
+ temp = &bands[i];
+ if (temp->min < temp->max) {
+ if (check >= temp->min && check <= temp->max)
+ break;
+ } else {
+ if (check >= temp->min || check <= temp->max)
+ break;
+ }
+ }
+ if (!bands[i].max)
+ return;
+ if (check > 1023)
+ return;
+ arfcn = check;
+ band = temp;
+ mode = last_mode;
+ refresh_display();
+ return;
+ }
+
+ if (cursor == 4)
+ return;
+
+ input[cursor] = code - KEY_0 + '0';
+ cursor++;
+ input[cursor] = '\0';
+ refresh_display();
+}
+
+static int inc_dec_arfcn(int inc)
+{
+ int i;
+
+ /* select current band */
+ for (i = 0; bands[i].max; i++) {
+ band = &bands[i];
+ if (band->min < band->max) {
+ if (arfcn >= band->min && arfcn <= band->max)
+ break;
+ } else {
+ if (arfcn >= band->min || arfcn <= band->max)
+ break;
+ }
+ }
+ if (!bands[i].max)
+ return -EINVAL;
+
+ if (inc) {
+ if (arfcn == band->max)
+ arfcn = band->next;
+ else if (arfcn == 1023)
+ arfcn = 0;
+ else
+ arfcn++;
+ } else {
+ if (arfcn == band->min)
+ arfcn = band->prev;
+ else if (arfcn == 0)
+ arfcn = 1023;
+ else
+ arfcn--;
+ }
+ /* select next band */
+ for (i = 0; bands[i].max; i++) {
+ band = &bands[i];
+ if (band->min < band->max) {
+ if (arfcn >= band->min && arfcn <= band->max)
+ break;
+ } else {
+ if (arfcn >= band->min || arfcn <= band->max)
+ break;
+ }
+ }
+ if (!bands[i].max)
+ return -EINVAL;
+
+ refresh_display();
+
+ return 0;
+}
+
+static void toggle_dcs_pcs(void)
+{
+ pcs = !pcs;
+ refresh_display();
+}
+
+static void toggle_up_down(void)
+{
+ uplink = !uplink;
+ refresh_display();
+}
+
+static void toggle_spectrum(void)
+{
+ if (mode == MODE_MAIN) {
+ mode = MODE_SPECTRUM;
+ pm_mode = PM_IDLE;
+ } else if (mode == MODE_SPECTRUM) {
+ mode = MODE_MAIN;
+ pm_mode = PM_IDLE;
+ }
+ l1s_reset();
+ l1s_reset_hw();
+ pm_count = 0;
+ refresh_display();
+}
+
+static void tone_inc_dec(int inc)
+{
+ if (inc) {
+ if (tone + 25 <= 255)
+ tone += 25;
+ } else {
+ if (tone - 25 >= 0)
+ tone -= 25;
+ }
+
+ refresh_display();
+}
+
+static void hold_max(void)
+{
+ max = !max;
+ max_power = power;
+ refresh_display();
+}
+
+static int inc_dec_spectrum(int inc)
+{
+ if (inc) {
+ pm_scale <<= 1;
+ if (pm_scale > 8)
+ pm_scale = 8;
+ } else {
+ pm_scale >>= 1;
+ if (pm_scale < 1)
+ pm_scale = 1;
+ }
+
+ refresh_display();
+
+ return 0;
+}
+
+static void handle_key_code()
+{
+ /* key repeat */
+ if (key_pressed) {
+ unsigned long elapsed = jiffies - key_pressed_when;
+ if (elapsed > key_pressed_delay) {
+ key_pressed_when = jiffies;
+ key_pressed_delay = 10;
+ /* only repeat these keys */
+ if (key_pressed_code == KEY_LEFT
+ || key_pressed_code == KEY_RIGHT)
+ key_code = key_pressed_code;
+ }
+ }
+
+ if (key_code == KEY_INV)
+ return;
+
+ /* do later, do not disturb tone */
+ if (tone_on)
+ return;
+
+ switch (key_code) {
+ case KEY_0:
+ case KEY_1:
+ case KEY_2:
+ case KEY_3:
+ case KEY_4:
+ case KEY_5:
+ case KEY_6:
+ case KEY_7:
+ case KEY_8:
+ case KEY_9:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM || mode == MODE_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_UP:
+ if (mode == MODE_MAIN)
+ tone_inc_dec(1);
+ else if (mode == MODE_SPECTRUM)
+ inc_dec_spectrum(1);
+ break;
+ case KEY_DOWN:
+ if (mode == MODE_MAIN)
+ tone_inc_dec(0);
+ else if (mode == MODE_SPECTRUM)
+ inc_dec_spectrum(0);
+ break;
+ case KEY_RIGHT:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
+ inc_dec_arfcn(1);
+ break;
+ case KEY_LEFT:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
+ inc_dec_arfcn(0);
+ break;
+ case KEY_LEFT_SB:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
+ toggle_dcs_pcs();
+ else if (mode == MODE_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_RIGHT_SB:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
+ toggle_up_down();
+ else if (mode == MODE_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_MENU:
+ hold_max();
+ break;
+ case KEY_POWER:
+ if (mode == MODE_ARFCN)
+ exit_arfcn();
+ else if (mode == MODE_SPECTRUM)
+ toggle_spectrum();
+ break;
+ case KEY_STAR:
+ if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
+ toggle_spectrum();
+ break;
+ default:
+ break;
+ }
+
+ key_code = KEY_INV;
+}
+
+static void handle_tone(void)
+{
+ unsigned long elapsed = jiffies - tone_time;
+
+ if (!tone_on) {
+ if (!tone || mode != MODE_MAIN)
+ return;
+ /* wait depending on power level */
+ if (elapsed < (uint8_t)(63-power))
+ return;
+ buzzer_volume(tone);
+ buzzer_note(NOTE(NOTE_C, OCTAVE_5));
+ tone_time = jiffies;
+ tone_on = 1;
+ return;
+ }
+
+ if (elapsed >= TONE_JIFFIES) {
+ tone_on = 0;
+ tone_time = jiffies;
+ buzzer_volume(0);
+ }
+}
+
+/* PM handling */
+
+static void handle_pm(void)
+{
+ /* start power measurement */
+ if (pm_mode == PM_IDLE && (mode == MODE_MAIN || mode == MODE_SPECTRUM)) {
+ struct msgb *msg = l1ctl_msgb_alloc(L1CTL_PM_REQ);
+ struct l1ctl_pm_req *pm;
+ uint16_t a, e;
+
+ pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm));
+ pm->type = 1;
+ if (mode == MODE_MAIN) {
+ a = arfcn;
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
+ a |= ARFCN_PCS;
+ if (uplink)
+ a |= ARFCN_UPLINK;
+ e = a;
+ pm_mode = PM_SENT;
+ }
+ if (mode == MODE_SPECTRUM) {
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
+ a = PCS_MIN | ARFCN_PCS;
+ e = PCS_MAX | ARFCN_PCS;
+ } else {
+ a = band->min;
+ e = band->max;
+ }
+ pm_mode = PM_RANGE_SENT;
+ }
+ if (uplink) {
+ a |= ARFCN_UPLINK;
+ e |= ARFCN_UPLINK;
+ }
+ pm->range.band_arfcn_from = htons(a);
+ pm->range.band_arfcn_to = htons(e);
+
+ l1a_l23_rx(SC_DLCI_L1A_L23, msg);
+
+ return;
+ }
+
+ if (pm_mode == PM_RESULT) {
+ pm_mode = PM_IDLE;
+ if (pm_count == pm_max) {
+ int i = 0;
+ int sum = 0;
+
+ if (uplink) {
+ /* find max */
+ for (i = 0; i < pm_count; i++) {
+ if (pm_meas[i] > sum)
+ sum = pm_meas[i];
+ }
+ power = sum;
+ } else {
+ for (i = 0; i < pm_count; i++)
+ sum += pm_meas[i];
+ power = sum / pm_count;
+ }
+ if (power > max_power)
+ max_power = power;
+ pm_count = 0;
+ pm_max = (uplink) ? NUM_PM_UL : NUM_PM_DL;
+ if (!tone_on)
+ refresh_display();
+ }
+ return;
+ }
+
+ if (pm_mode == PM_RANGE_RESULT) {
+ pm_mode = PM_IDLE;
+ refresh_display();
+ buzzer_volume(tone);
+ buzzer_note(NOTE(NOTE_C, OCTAVE_5));
+ tone_time = jiffies;
+ tone_on = 1;
+ return;
+ }
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+/* note: called from IRQ context */
+static void l1a_l23_tx(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
+ struct l1ctl_pm_conf *pmr;
+
+ switch (l1h->msg_type) {
+ case L1CTL_PM_CONF:
+ if (pm_mode == PM_SENT) {
+ pmr = (struct l1ctl_pm_conf *) l1h->data;
+ pm_meas[pm_count] = pmr->pm[0];
+ pm_count++;
+ pm_mode = PM_RESULT;
+ }
+ if (pm_mode == PM_RANGE_SENT) {
+ for (pmr = (struct l1ctl_pm_conf *) l1h->data;
+ (uint8_t *) pmr < msg->tail; pmr++) {
+ if (!max || pm_spectrum[ntohs(pmr->band_arfcn) & 1023] < pmr->pm[0])
+ pm_spectrum[ntohs(pmr->band_arfcn) & 1023] = pmr->pm[0];
+ }
+ if ((l1h->flags & L1CTL_F_DONE))
+ pm_mode = PM_RANGE_RESULT;
+ }
+ l1s.tpu_offset_correction += 5000 / NUM_PM_UL;
+ break;
+ }
+
+ msgb_free(msg);
+
+}
+
+static void console_rx_cb(uint8_t dlci, struct msgb *msg)
+{
+ if (dlci != SC_DLCI_CONSOLE) {
+ printf("Message for unknown DLCI %u\n", dlci);
+ return;
+ }
+
+ printf("Message on console DLCI: '%s'\n", msg->data);
+ msgb_free(msg);
+}
+
+static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
+{
+ int i;
+ printf("l1a_l23_rx_cb (DLCI %d): ", dlci);
+ for (i = 0; i < msg->len; i++)
+ printf("%02x ", msg->data[i]);
+ puts("\n");
+}
+
+static void key_handler(enum key_codes code, enum key_states state)
+{
+ if (state != PRESSED) {
+ key_pressed = 0;
+ return;
+ }
+ /* key repeat */
+ if (!key_pressed) {
+ key_pressed = 1;
+ key_pressed_when = jiffies;
+ key_pressed_code = code;
+ key_pressed_delay = 60;
+ }
+
+ key_code = code;
+}
+
+int main(void)
+{
+ board_init();
+
+ puts("\n\nOSMOCOM Monitor Tool (revision " GIT_REVISION ")\n");
+ puts(hr);
+
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ /* Dump clock config before PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ keypad_set_handler(&key_handler);
+
+ /* Dump clock config after PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb);
+ sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
+
+ layer1_init();
+ l1a_l23_tx_cb = l1a_l23_tx;
+
+// display_unset_attr(DISP_ATTR_INVERT);
+
+ tpu_frame_irq_en(1, 1);
+
+ buzzer_mode_pwt(1);
+ buzzer_volume(0);
+
+ memset(pm_spectrum, 0, sizeof(pm_spectrum));
+
+ /* inc 0 to 1 and refresh */
+ inc_dec_arfcn(1);
+
+ while (1) {
+ l1a_compl_execute();
+ osmo_timers_update();
+ handle_key_code();
+ l1a_l23_handler();
+ handle_pm();
+ handle_tone();
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+