diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/target/firmware/Makefile | 2 | ||||
-rw-r--r-- | src/target/firmware/apps/rssi/main.c | 1282 | ||||
-rw-r--r-- | src/target/firmware/board/compal/highram.lds | 4 | ||||
-rw-r--r-- | src/target/firmware/board/compal/ram.lds | 4 | ||||
-rw-r--r-- | src/target/firmware/include/layer1/sync.h | 1 | ||||
-rw-r--r-- | src/target/firmware/layer1/l23_api.c | 7 | ||||
-rw-r--r-- | src/target/firmware/layer1/mframe_sched.c | 6 | ||||
-rw-r--r-- | src/target/firmware/layer1/prim_pm.c | 4 |
8 files changed, 1301 insertions, 9 deletions
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile index 89a3d0bf..ffdfe5d2 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 rssi # Framebuffer support, board specific drivers # diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c new file mode 100644 index 00000000..ea8bbb49 --- /dev/null +++ b/src/target/firmware/apps/rssi/main.c @@ -0,0 +1,1282 @@ +/* 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> +#include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm48_ie.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_SYNC, +} mode = MODE_MAIN; +enum mode last_mode; /* where to return after entering ARFCN */ + +static uint16_t arfcn = 0, ul_arfcn; +int pcs = 0; +int uplink = 0; +int max = 0; +uint8_t power, max_power; +char input[5]; +int cursor; + +char *sync_result = NULL; +char *sync_msg = ""; + +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; + +uint8_t bsic; +uint8_t ul_levels[8], ul_max[8]; /* 8 uplink levels */ +uint8_t si_1[23]; +uint8_t si_2[23]; +uint8_t si_2bis[23]; +uint8_t si_2ter[23]; +uint8_t si_3[23]; +uint8_t si_4[23]; +uint16_t si_new = 0, ul_new; +uint16_t mcc, mnc, lac, cell_id; +int nb_num; +struct gsm_sysinfo_freq freq[1024]; +#define NEIGH_LINES ((framebuffer->height - 25) / 8) + +#define FREQ_TYPE_SERV 0x01 /* frequency of the serving cell */ +#define FREQ_TYPE_NCELL 0x1c /* frequency of the neighbor cell */ +#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ +#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ +#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ + +/* UI */ + +static void print_display(char *text, int *y, int c) +{ + /* skip lines, given by cursor */ + (*y)++; + if (c >= (*y)) + return; + /* skip, if end of display area is reached */ + if ((*y) - c > NEIGH_LINES) + return; + + fb_gotoxy(0, 20 + (((*y) - c - 1) << 3)); + fb_putstr(text, framebuffer->width); +} + +static void refresh_display(void) +{ + char text[16]; + + fb_clear(); + + /* header */ + fb_setbg(FB_COLOR_WHITE); + if (mode != MODE_SPECTRUM && !(mode == MODE_SYNC && cursor < 0)) { + fb_setfg(FB_COLOR_BLUE); + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(0, 7); + fb_putstr("Osmocom Monitor Tool",-1); + fb_gotoxy(0, 10); + fb_setfg(FB_COLOR_GREEN); + fb_boxto(framebuffer->width - 1, 10); + } + fb_setfg(FB_COLOR_BLACK); + fb_setfont(FB_FONT_C64); + + /* SYNC / UL levels */ + if (mode == MODE_SYNC && cursor < 0) { + int i, tn, l; + int offset = (framebuffer->width - 96) >> 2; + int height = framebuffer->height - 25; + + fb_setfont(FB_FONT_HELVR08); + for (i = 0; i < 8; i++) { + if (uplink) + tn = (i + 3) & 7; /* UL is shifted by 3 */ + else + tn = i; + fb_setbg(FB_COLOR_WHITE); + fb_gotoxy(offset + 12 * i, 7); + l = (max) ? ul_max[tn] : ul_levels[tn]; + l = 110 - l; + if (l >= 100) + l -= 100; + sprintf(text, "%02d", l); + fb_putstr(text,framebuffer->width); + fb_setbg(FB_COLOR_BLACK); + fb_gotoxy(offset + 3 + 12 * i, height + 10); + fb_boxto(offset + 3 + 12 * i + 5, height + 10 - ul_levels[tn] * height / 64); + if (max) { + fb_gotoxy(offset + 3 + 12 * i, height + 10 - ul_max[tn] * height / 64); + fb_boxto(offset + 3 + 12 * i + 5, height + 10 - ul_max[tn] * height / 64); + } + } + fb_setbg(FB_COLOR_TRANSP); + if (max) { + fb_setfg(FB_COLOR_RED); + fb_gotoxy(framebuffer->width - 16, 15); + fb_putstr("max", framebuffer->width); + } + fb_setfont(FB_FONT_C64); + fb_setfg(FB_COLOR_BLUE); + fb_gotoxy(0, 16); + if (pcs && ul_arfcn >= PCS_MIN && ul_arfcn <= PCS_MAX) + sprintf(text, "%4dP", ul_arfcn); + else if (ul_arfcn >= DCS_MIN && ul_arfcn <= DCS_MAX) + sprintf(text, "%4dD", ul_arfcn); + else + sprintf(text, "%4d ", ul_arfcn); + fb_putstr(text, framebuffer->width); + fb_setbg(FB_COLOR_WHITE); + fb_setfg(FB_COLOR_BLACK); + } + + /* SYNC / SI */ + if (mode == MODE_SYNC && cursor == 0) { + fb_gotoxy(0, 20); + if (sync_msg[0] == 'o') + sprintf(text, "BSIC%d/%d %4d", bsic >> 3, bsic & 7, + power - 110); + else + sprintf(text, "Sync %s", sync_msg); + fb_putstr(text, -1); + + fb_gotoxy(0,28); + text[0] = si_1[2] ? '1' : '-'; + text[1] = ' '; + text[2] = si_2[2] ? '2' : '-'; + text[3] = ' '; + text[4] = si_2bis[2] ? '2' : '-'; + text[5] = si_2bis[2] ? 'b' : ' '; + text[6] = si_2ter[2] ? '2' : '-'; + text[7] = si_2ter[2] ? 't' : ' '; + text[8] = ' '; + text[9] = si_3[2] ? '3' : '-'; + text[10] = ' '; + text[11] = si_4[2] ? '4' : '-'; + text[12] = '\0'; + fb_putstr(text, -1); + + fb_gotoxy(0, 36); + fb_putstr("MCC MNC LAC ", -1); + fb_gotoxy(0, 44); + if (mcc) { + if ((mnc & 0x00f) == 0x00f) + sprintf(text, "%3x %02x %04x", mcc, mnc >> 4, lac); + else + sprintf(text, "%3x %03x %04x", mcc, mnc, lac); + fb_putstr(text, -1); + } else + fb_putstr("--- --- ----", -1); + fb_gotoxy(0, 52); + if (si_3[2]) { + sprintf(text, "cell id:%04x", cell_id); + fb_putstr(text, -1); + } else + fb_putstr("cell id:----", -1); + } + + /* SYNC / neighbour cells */ + if (mode == MODE_SYNC && cursor > 0) { + int i, y = 0; + + text[0] = '\0'; + for (i = 0; i < 1024; i++) { + if (freq[i].mask & FREQ_TYPE_SERV) { + if (!text[0]) + sprintf(text, "S: %4d", i); + else { + sprintf(text + 7, " %4d", i); + print_display(text, &y, cursor - 1); + text[0] = '\0'; + } + } + } + if (text[0]) + print_display(text, &y, cursor - 1); + text[0] = '\0'; + for (i = 0; i < 1024; i++) { + if (freq[i].mask & FREQ_TYPE_NCELL) { + if (!text[0]) + sprintf(text, "N: %4d", i); + else { + sprintf(text + 7, " %4d", i); + print_display(text, &y, cursor - 1); + text[0] = '\0'; + } + } + } + if (text[0]) + print_display(text, &y, cursor - 1); + nb_num = y; + } + + /* 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_setfg(FB_COLOR_RED); + fb_gotoxy(framebuffer->width - 16, 39); + fb_putstr("max", framebuffer->width); + fb_setfont(FB_FONT_C64); + fb_setfg(FB_COLOR_BLACK); + } + 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; + int height = framebuffer->height - 25; + + 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, " %4d", pm_spectrum[arfcn & 1023] - 110); + fb_putstr(text, -1); + fb_setfg(FB_COLOR_RED); + if (max) { + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(framebuffer->width - 16,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); + } + fb_setfg(FB_COLOR_BLACK); + 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; 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 * height / 64); + if (p > height) + p = height; + if (i == (framebuffer->width >> 1)) + fb_setfg(FB_COLOR_RED); + fb_gotoxy(i, height + 10 - p); + fb_boxto(i, height + 10); + if (i == (framebuffer->width >> 1)) + fb_setfg(FB_COLOR_BLACK); + } + i = framebuffer->width >> 1; + fb_gotoxy(i, 0); + fb_boxto(i, 4); + fb_gotoxy(i, height + 10); + fb_boxto(i, height + 14); + } + + /* footer */ + fb_setfg(FB_COLOR_GREEN); + fb_gotoxy(0, framebuffer->height - 10); + fb_boxto(framebuffer->width-1, framebuffer->height - 10); + fb_gotoxy(0, framebuffer->height - 1); + fb_setfg(FB_COLOR_RED); + if (mode == MODE_ARFCN) + sprintf(text, "%s %s", (cursor) ? "del " : "back", + (cursor) ? "enter" : " "); + else if (mode == MODE_SYNC && cursor < 0) + sprintf(text, "%s %s", "back", + (uplink) ? "UL" : "DL"); + else if (mode == MODE_SYNC) + sprintf(text, "%s ", "back"); + else + sprintf(text, "%s %s", (pcs) ? "PCS" : "DCS", + (uplink) ? "UL" : "DL"); + fb_putstr(text, -1); + fb_setfg(FB_COLOR_BLACK); + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(0, framebuffer->height - 2); + 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 request_ul_levels(uint16_t a); + +static int inc_dec_ul_arfcn(int inc) +{ + uint16_t a; + + /* loop until we hit a serving cell or our current bcch arfcn */ + if (inc) { + for (a = (ul_arfcn + 1) & 1023; a != (arfcn & 1023); + a = (a + 1) & 1023) { + if ((freq[a].mask & FREQ_TYPE_SERV)) + break; + } + } else { + for (a = (ul_arfcn - 1) & 1023; a != (arfcn & 1023); + a = (a - 1) & 1023) { + if ((freq[a].mask & FREQ_TYPE_SERV)) + break; + } + } + ul_arfcn = a; + + refresh_display(); + + request_ul_levels(a); + + return 0; +} + +static void toggle_dcs_pcs(void) +{ + pcs = !pcs; + refresh_display(); +} + +static void toggle_up_down(void) +{ + uplink = !uplink; + refresh_display(); + + if (mode == MODE_SYNC && cursor < 0) + request_ul_levels(ul_arfcn); +} + +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_neighbour(int inc) +{ + if (inc) { + if (cursor > 0 && cursor - 1 >= (nb_num - NEIGH_LINES)) + return -EINVAL; + cursor++; + } else { + if (cursor < 0) + return -EINVAL; + cursor--; + } + + refresh_display(); + + return 0; +} + +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 enter_sync(void); +static void exit_sync(void); + +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_SYNC) + inc_dec_neighbour(0); + 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_SYNC) + inc_dec_neighbour(1); + else if (mode == MODE_SPECTRUM) + inc_dec_spectrum(0); + break; + case KEY_RIGHT: + if (mode == MODE_MAIN || mode == MODE_SPECTRUM) + inc_dec_arfcn(1); + else if (mode == MODE_SYNC && cursor < 0) + inc_dec_ul_arfcn(1); + break; + case KEY_LEFT: + if (mode == MODE_MAIN || mode == MODE_SPECTRUM) + inc_dec_arfcn(0); + else if (mode == MODE_SYNC && cursor < 0) + inc_dec_ul_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); + else if (mode == MODE_SYNC) + exit_sync(); + break; + case KEY_RIGHT_SB: + if (mode == MODE_MAIN || mode == MODE_SPECTRUM) + toggle_up_down(); + else if (mode == MODE_ARFCN) + enter_arfcn(key_code); + else if (mode == MODE_SYNC && cursor < 0) + toggle_up_down(); + break; + case KEY_OK: + if (mode == MODE_MAIN || mode == MODE_SPECTRUM) + enter_sync(); + else if (mode == MODE_SYNC) + exit_sync(); + break; + case KEY_MENU: + hold_max(); + break; + case KEY_POWER: + if (mode == MODE_ARFCN) + exit_arfcn(); + else if (mode == MODE_SYNC) + exit_sync(); + 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; + } +} + +/* sync / SI */ + +static void enter_sync(void) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_FBSB_REQ); + struct l1ctl_fbsb_req *req; + uint16_t a = arfcn; + + l1s_reset(); + l1s_reset_hw(); + pm_count = 0; + pm_mode = PM_IDLE; + + req = (struct l1ctl_fbsb_req *) msgb_put(msg, sizeof(*req)); + if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) + a |= ARFCN_PCS; + req->band_arfcn = htons(a); + req->timeout = htons(100); + /* Threshold when to consider FB_MODE1: 4kHz - 1kHz */ + req->freq_err_thresh1 = htons(11000 - 1000); + /* Threshold when to consider SCH: 1kHz - 200Hz */ + req->freq_err_thresh2 = htons(1000 - 200); + /* not used yet! */ + req->num_freqerr_avg = 3; + req->flags = L1CTL_FBSB_F_FB01SB; + req->sync_info_idx = 0; + req->ccch_mode = CCCH_MODE_NONE; + l1a_l23_rx(SC_DLCI_L1A_L23, msg); + + mode = MODE_SYNC; + memset(ul_levels, 0, sizeof(ul_levels)); + si_new = 0; + ul_new = 0; + ul_arfcn = arfcn; + si_1[2] = 0; + si_2[2] = 0; + si_2bis[2] = 0; + si_2ter[2] = 0; + si_3[2] = 0; + si_4[2] = 0; + mcc = mnc = lac = 0; + memset(freq, 0, sizeof(freq)); + cursor = 0; + nb_num = 0; + sync_msg = "trying"; + refresh_display(); +} + +static void exit_sync(void) +{ + l1s_reset(); + l1s_reset_hw(); + pm_count = 0; + pm_mode = PM_IDLE; + mode = MODE_MAIN; +} + +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *_mcc, +uint16_t *_mnc, uint16_t *_lac) +{ + *_mcc = ((lai->digits[0] & 0x0f) << 8) + | (lai->digits[0] & 0xf0) + | (lai->digits[1] & 0x0f); + *_mnc = ((lai->digits[2] & 0x0f) << 8) + | (lai->digits[2] & 0xf0) + | ((lai->digits[1] & 0xf0) >> 4); + *_lac = ntohs(lai->lac); + + return 0; +} + +static void request_ul_levels(uint16_t a) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_REQ); + struct l1ctl_neigh_pm_req *pm_req = + (struct l1ctl_neigh_pm_req *) msgb_put(msg, sizeof(*pm_req)); + int i; + + if (pcs && a >= PCS_MIN && a <= PCS_MAX) + a |= ARFCN_PCS; + if (uplink) + a |= ARFCN_UPLINK; + pm_req->n = 8; + for (i = 0; i < 8; i++) { + pm_req->band_arfcn[i] = htons(a); + pm_req->tn[i] = i; + } + l1a_l23_rx(SC_DLCI_L1A_L23, msg); +} + +static void handle_sync(void) +{ + struct gsm48_system_information_type_1 *si1; + struct gsm48_system_information_type_2 *si2; + struct gsm48_system_information_type_2bis *si2bis; + struct gsm48_system_information_type_2ter *si2ter; + struct gsm48_system_information_type_3 *si3; + struct gsm48_system_information_type_4 *si4; + + if (mode != MODE_SYNC) + return; + + /* once we synced, we take the result and request UL measurement */ + if (sync_result) { + uint16_t a = ul_arfcn; + + sync_msg = sync_result; + sync_result = NULL; + refresh_display(); + + if (sync_msg[0] != 'o') + return; + + request_ul_levels(a); + + return; + } + + if (tone_on) + return; + + /* no UL result, no SI result */ + if (!ul_new && !(si_new & 0x100)) + return; + + /* new UL result */ + if (ul_new) { + ul_new = 0; + if (cursor < 0) + refresh_display(); + return; + } + + /* decode si */ + switch (si_new & 0xff) { + case GSM48_MT_RR_SYSINFO_1: + si1 = (struct gsm48_system_information_type_1 *)si_1; + gsm48_decode_freq_list(freq, si1->cell_channel_description, + sizeof(si1->cell_channel_description), 0xce, + FREQ_TYPE_SERV); + break; + case GSM48_MT_RR_SYSINFO_2: + si2 = (struct gsm48_system_information_type_2 *)si_2; + gsm48_decode_freq_list(freq, si2->bcch_frequency_list, + sizeof(si2->bcch_frequency_list), 0xce, + FREQ_TYPE_NCELL_2); + break; + case GSM48_MT_RR_SYSINFO_2bis: + si2bis = (struct gsm48_system_information_type_2bis *)si_2bis; + gsm48_decode_freq_list(freq, si2bis->bcch_frequency_list, + sizeof(si2bis->bcch_frequency_list), 0xce, + FREQ_TYPE_NCELL_2bis); + break; + case GSM48_MT_RR_SYSINFO_2ter: + si2ter = (struct gsm48_system_information_type_2ter *)si_2ter; + gsm48_decode_freq_list(freq, si2ter->ext_bcch_frequency_list, + sizeof(si2ter->ext_bcch_frequency_list), 0x8e, + FREQ_TYPE_NCELL_2ter); + break; + case GSM48_MT_RR_SYSINFO_3: + si3 = (struct gsm48_system_information_type_3 *)si_3; + gsm48_decode_lai(&si3->lai, &mcc, &mnc, &lac); + cell_id = ntohs(si3->cell_identity); + break; + case GSM48_MT_RR_SYSINFO_4: + si4 = (struct gsm48_system_information_type_4 *)si_4; + gsm48_decode_lai(&si4->lai, &mcc, &mnc, &lac); + break; + } + + if (cursor >= 0) + refresh_display(); + + /* tone depends on successfully received BCCH */ + buzzer_volume(tone); + tone_time = jiffies; + tone_on = 1; + if ((si_new & 0xff) == 0xff) + buzzer_note(NOTE(NOTE_C, OCTAVE_2)); + else + buzzer_note(NOTE(NOTE_C, OCTAVE_5)); + si_new = 0; +} + +/* 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; + struct l1ctl_info_dl *dl; + struct l1ctl_fbsb_conf *sb; + uint8_t chan_type, chan_ts, chan_ss; + struct gsm48_system_information_type_header *sih; + struct l1ctl_neigh_pm_ind *pm_ind; + + 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; + case L1CTL_FBSB_CONF: + dl = (struct l1ctl_info_dl *) l1h->data; + sb = (struct l1ctl_fbsb_conf *) dl->payload; + if (sb->result == 0) + sync_result = "ok"; + else + sync_result = "error"; + bsic = sb->bsic; + break; + case L1CTL_DATA_IND: + dl = (struct l1ctl_info_dl *) l1h->data; + msg->l2h = dl->payload; + rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts); + if (chan_type != RSL_CHAN_BCCH) + break; + msg->l3h = msg->l2h; + + power = dl->rx_level; + if (dl->fire_crc >= 2) { + si_new = 0x1ff; /* error frame indication */ + break; + } + + /* store SI */ + sih = msgb_l3(msg); + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_1: + memcpy(si_1, msgb_l3(msg), msgb_l3len(msg)); + break; + case GSM48_MT_RR_SYSINFO_2: + memcpy(si_2, msgb_l3(msg), msgb_l3len(msg)); + break; + case GSM48_MT_RR_SYSINFO_2bis: + memcpy(si_2bis, msgb_l3(msg), msgb_l3len(msg)); + break; + case GSM48_MT_RR_SYSINFO_2ter: + memcpy(si_2ter, msgb_l3(msg), msgb_l3len(msg)); + break; + case GSM48_MT_RR_SYSINFO_3: + memcpy(si_3, msgb_l3(msg), msgb_l3len(msg)); + break; + case GSM48_MT_RR_SYSINFO_4: + memcpy(si_4, msgb_l3(msg), msgb_l3len(msg)); + break; + } + si_new = sih->system_information | 0x100; + sercomm_sendmsg(SC_DLCI_L1A_L23, msg); + return; /* msg is freed by sercom */ + case L1CTL_NEIGH_PM_IND: + for (pm_ind = (struct l1ctl_neigh_pm_ind *) l1h->data; + (uint8_t *) pm_ind < msg->tail; pm_ind++) { + ul_levels[pm_ind->tn] = pm_ind->pm[0]; + /* hold max only, if max enabled and level is lower */ + if (!max || ul_levels[pm_ind->tn] > ul_max[pm_ind->tn]) + ul_max[pm_ind->tn] = ul_levels[pm_ind->tn]; + if (pm_ind->tn == 7) + ul_new = 1; + } + 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)); + memset(ul_max, 0, sizeof(ul_max)); + + /* 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_sync(); + handle_tone(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + diff --git a/src/target/firmware/board/compal/highram.lds b/src/target/firmware/board/compal/highram.lds index 1f0a5a67..498a2fa1 100644 --- a/src/target/firmware/board/compal/highram.lds +++ b/src/target/firmware/board/compal/highram.lds @@ -16,9 +16,9 @@ MEMORY /* lowram: could be anything, we place exception vectors here */ XRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00020000 /* highram binary: our text, initialized data */ - LRAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00010000 + LRAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00014000 /* highram binary: our unitialized data, stacks, heap */ - IRAM (rw) : ORIGIN = 0x00830000, LENGTH = 0x00010000 + IRAM (rw) : ORIGIN = 0x00834000, LENGTH = 0x0000c000 } SECTIONS { diff --git a/src/target/firmware/board/compal/ram.lds b/src/target/firmware/board/compal/ram.lds index 342870dc..9503edee 100644 --- a/src/target/firmware/board/compal/ram.lds +++ b/src/target/firmware/board/compal/ram.lds @@ -11,9 +11,9 @@ ENTRY(_start) MEMORY { /* compal-loaded binary: our text, initialized data */ - LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00014000 /* compal-loaded binary: our unitialized data, stacks, heap */ - IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000 + IRAM (rw) : ORIGIN = 0x00814000, LENGTH = 0x0000c000 } SECTIONS { diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index aa03c827..dae85a1a 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -151,6 +151,7 @@ struct l1s_state { uint8_t pos; uint8_t running; uint16_t band_arfcn[64]; + uint8_t tn[64]; uint8_t level[64]; } neigh_pm; }; diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index 2581b5bb..659f3173 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -49,7 +49,8 @@ /* the size we will allocate struct msgb* for HDLC */ #define L3_MSG_HEAD 4 -#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr)+sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_traffic_ind) + L3_MSG_HEAD) +#define L3_MSG_DATA 200 +#define L3_MSG_SIZE (L3_MSG_HEAD + sizeof(struct l1ctl_hdr) + L3_MSG_DATA) void (*l1a_l23_tx_cb)(struct msgb *msg) = NULL; @@ -529,8 +530,10 @@ static void l1ctl_rx_neigh_pm_req(struct msgb *msg) /* now reset pointer and fill list */ l1s.neigh_pm.pos = 0; l1s.neigh_pm.running = 0; - for (i = 0; i < pm_req->n; i++) + for (i = 0; i < pm_req->n; i++) { l1s.neigh_pm.band_arfcn[i] = ntohs(pm_req->band_arfcn[i]); + l1s.neigh_pm.tn[i] = pm_req->tn[i]; + } printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req->n); l1s.neigh_pm.n = pm_req->n; /* atomic */ diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 6281c3d6..2367d425 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -200,7 +200,11 @@ static const struct mframe_sched_item mf_sdcch8_7[] = { /* Measurement for MF 51 */ static const struct mframe_sched_item mf_neigh_pm51[] = { - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 50 }, + { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 0 }, + { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 10 }, + { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 20 }, + { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 30 }, + { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 40 }, { .sched_set = NULL } }; diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c index 07b72097..1630600a 100644 --- a/src/target/firmware/layer1/prim_pm.c +++ b/src/target/firmware/layer1/prim_pm.c @@ -175,7 +175,8 @@ static int l1s_neigh_pm_cmd(uint8_t num_meas, * 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); + 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); @@ -218,6 +219,7 @@ static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2, 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; } |