summaryrefslogtreecommitdiffstats
path: root/src/target
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2012-01-26 18:58:09 +0100
committerHarald Welte <laforge@gnumonks.org>2012-02-04 00:04:58 +0100
commitc338fc3343e3a01c26030ddebb9b42ac6a5b3179 (patch)
tree04890fdbc8677270778afd5df7b3ac5231c49eac /src/target
parente27c6925b0caa3db235d6811fc75cc04cc1e739a (diff)
Adding "monitor" application to firmware
This app is used to measure receive level of given channels. By pressing digits, the channel can be selected. By pressing left or right button, the frequency is increased/decreased. By pressing the menu button, the maximum received level is shown until pressing menu button again. (usefull for hopping) By pressing up or down button, the volume of a tone is changed, that indicates rx level. The left soft button is used to toggle PCS/DCS on shared channel numbers. The right soft button is used to toggle uplink and downlink.
Diffstat (limited to 'src/target')
-rw-r--r--src/target/firmware/Makefile2
-rw-r--r--src/target/firmware/apps/monitor/main.c598
2 files changed, 599 insertions, 1 deletions
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile
index 89a3d0bf..de1e71ea 100644
--- a/src/target/firmware/Makefile
+++ b/src/target/firmware/Makefile
@@ -4,7 +4,7 @@
BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 gta0x pirelli_dpl10
# List of all applications (meant to be overridden on command line)
-APPLICATIONS?=hello_world compal_dsp_dump layer1 loader chainload
+APPLICATIONS?=hello_world compal_dsp_dump layer1 loader chainload monitor
# Framebuffer support, board specific drivers
#
diff --git a/src/target/firmware/apps/monitor/main.c b/src/target/firmware/apps/monitor/main.c
new file mode 100644
index 00000000..afd9a055
--- /dev/null
+++ b/src/target/firmware/apps/monitor/main.c
@@ -0,0 +1,598 @@
+/* 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;
+
+enum mode {
+ MODE_MAIN,
+ MODE_ARFCN,
+} mode = MODE_MAIN;
+
+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_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;
+
+#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_setfg(FB_COLOR_BLUE);
+ fb_setbg(FB_COLOR_WHITE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(0,6);
+ fb_putstr("Osmocom Montitor Tool",-1);
+ fb_gotoxy(0,10);
+ fb_setfg(FB_COLOR_BLACK);
+ fb_boxto(framebuffer->width-1,10);
+
+ 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_gotoxy(0,45);
+ fb_boxto(framebuffer->width * power / 64, 46);
+ fb_gotoxy(0,47);
+ fb_boxto(framebuffer->width * power / 64, 48);
+ fb_gotoxy(0,49);
+ fb_boxto(framebuffer->width * power / 64, 50);
+ }
+
+ /* 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 = MODE_MAIN;
+ refresh_display();
+}
+
+static void enter_arfcn(enum key_codes code)
+{
+ /* enter mode */
+ if (mode != MODE_ARFCN) {
+ 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 (!temp->max)
+ return;
+ if (check > 1023)
+ return;
+ arfcn = check;
+ band = temp;
+ mode = MODE_MAIN;
+ 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 (!band->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 (!band->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 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;
+}
+
+static void handle_key_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_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_UP:
+ tone_inc_dec(1);
+ break;
+ case KEY_DOWN:
+ tone_inc_dec(0);
+ break;
+ case KEY_RIGHT:
+ if (mode == MODE_MAIN)
+ inc_dec_arfcn(1);
+ break;
+ case KEY_LEFT:
+ if (mode == MODE_MAIN)
+ inc_dec_arfcn(0);
+ break;
+ case KEY_LEFT_SB:
+ if (mode == MODE_MAIN)
+ toggle_dcs_pcs();
+ else if (mode == MODE_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_RIGHT_SB:
+ if (mode == MODE_MAIN)
+ 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();
+ 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) {
+ struct msgb *msg = l1ctl_msgb_alloc(L1CTL_PM_REQ);
+ struct l1ctl_pm_req *pm;
+ unsigned short a = arfcn;
+
+ pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm));
+ pm->type = 1;
+ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
+ a |= ARFCN_PCS;
+ if (uplink)
+ a |= ARFCN_UPLINK;
+ pm->range.band_arfcn_from = htons(a);
+ pm->range.band_arfcn_to = htons(a);
+
+ l1a_l23_rx(SC_DLCI_L1A_L23, msg);
+
+ pm_mode = PM_SENT;
+ 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;
+ }
+}
+
+/* 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:
+ pmr = (struct l1ctl_pm_conf *) l1h->data;
+ pm_meas[pm_count] = pmr->pm[0];
+ pm_count++;
+ pm_mode = PM_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)
+ return;
+
+ 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);
+
+ /* 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();
+}
+