summaryrefslogtreecommitdiffstats
path: root/src/target/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'src/target/firmware')
-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();
+}
+