1563 lines
34 KiB
C
1563 lines
34 KiB
C
/* 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>
|
|
#include <battery/battery.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_RACH,
|
|
} 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 ((HZ < 25) ? 1 : HZ / 25)
|
|
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 ccch_conf;
|
|
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 */
|
|
|
|
int rach = 0;
|
|
struct gsm48_req_ref rach_ref;
|
|
uint8_t rach_ra;
|
|
unsigned long rach_when;
|
|
uint8_t ta;
|
|
|
|
enum assign {
|
|
ASSIGN_NONE,
|
|
ASSIGN_NO_TX,
|
|
ASSIGN_RESULT,
|
|
ASSIGN_REJECT,
|
|
ASSIGN_TIMEOUT,
|
|
} assign;
|
|
|
|
/* 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];
|
|
int bat = battery_info.battery_percent;
|
|
|
|
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 RSSI", -1);
|
|
fb_setfg(FB_COLOR_RGB(0xc0, 0xc0, 0x00));
|
|
fb_setfont(FB_FONT_SYMBOLS);
|
|
fb_gotoxy(framebuffer->width - 15, 8);
|
|
if (bat >= 100 && (battery_info.flags & BATTERY_CHG_ENABLED)
|
|
&& !(battery_info.flags & BATTERY_CHARGING))
|
|
fb_putstr("@HHBC", framebuffer->width);
|
|
else {
|
|
sprintf(text, "@%c%c%cC", (bat >= 30) ? 'B':'A',
|
|
(bat >= 60) ? 'B':'A', (bat >= 90) ? 'B':'A');
|
|
fb_putstr(text, framebuffer->width);
|
|
}
|
|
fb_gotoxy(0, 8);
|
|
sprintf(text, "%c%cE%c%c", (power >= 40) ? 'D':'G',
|
|
(power >= 10) ? 'D':'G', (power >= 10) ? 'F':'G',
|
|
(power >= 40) ? 'F':'G');
|
|
fb_putstr(text, framebuffer->width);
|
|
fb_setfg(FB_COLOR_GREEN);
|
|
fb_gotoxy(0, 10);
|
|
fb_boxto(framebuffer->width - 1, 10);
|
|
}
|
|
fb_setfg(FB_COLOR_BLACK);
|
|
fb_setfont(FB_FONT_C64);
|
|
|
|
/* RACH */
|
|
if (mode == MODE_RACH) {
|
|
unsigned long elapsed = jiffies - rach_when;
|
|
|
|
fb_gotoxy(0,28);
|
|
switch (assign) {
|
|
case ASSIGN_NONE:
|
|
fb_putstr("Rach sent...", -1);
|
|
break;
|
|
case ASSIGN_RESULT:
|
|
sprintf(text, "TA = %d", ta);
|
|
fb_putstr(text, -1);
|
|
fb_gotoxy(0,36);
|
|
sprintf(text, "(%dm)", ta * 554);
|
|
fb_putstr(text, -1);
|
|
break;
|
|
case ASSIGN_REJECT:
|
|
fb_putstr("Rejected!", -1);
|
|
break;
|
|
case ASSIGN_NO_TX:
|
|
fb_putstr("TX disabled", -1);
|
|
break;
|
|
case ASSIGN_TIMEOUT:
|
|
fb_putstr("Timeout", -1);
|
|
break;
|
|
}
|
|
switch (assign) {
|
|
case ASSIGN_RESULT:
|
|
case ASSIGN_REJECT:
|
|
fb_gotoxy(0,44);
|
|
sprintf(text, "Delay:%ldms", elapsed * 1000 / HZ);
|
|
fb_putstr(text, -1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* SYNC / UL levels */
|
|
if (mode == MODE_SYNC && cursor < 0) {
|
|
int i, tn, l;
|
|
int offset = (framebuffer->width - 96) >> 1;
|
|
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 || mode == MODE_RACH)
|
|
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 enter_rach(void);
|
|
static void exit_rach(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 = HZ / 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();
|
|
else if (mode == MODE_RACH)
|
|
exit_rach();
|
|
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 || mode == MODE_RACH)
|
|
enter_rach();
|
|
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_RACH)
|
|
exit_rach();
|
|
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;
|
|
ccch_conf = -1;
|
|
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);
|
|
if (ccch_conf < 0) {
|
|
struct msgb *msg =
|
|
l1ctl_msgb_alloc(L1CTL_CCCH_MODE_REQ);
|
|
struct l1ctl_ccch_mode_req *req =
|
|
(struct l1ctl_ccch_mode_req *)
|
|
msgb_put(msg, sizeof(*req));
|
|
|
|
ccch_conf = si3->control_channel_desc.ccch_conf;
|
|
req->ccch_mode = (ccch_conf == 1)
|
|
? CCCH_MODE_COMBINED
|
|
: CCCH_MODE_NON_COMBINED;
|
|
printf("ccch_mode=%d\n", ccch_conf);
|
|
|
|
l1a_l23_rx(SC_DLCI_L1A_L23, msg);
|
|
}
|
|
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;
|
|
}
|
|
|
|
static void enter_rach(void)
|
|
{
|
|
if (ccch_conf < 0)
|
|
return;
|
|
|
|
if (rach)
|
|
return;
|
|
|
|
#ifndef CONFIG_TX_ENABLE
|
|
assign = ASSIGN_NO_TX;
|
|
mode = MODE_RACH;
|
|
/* display refresh is done by rach handler */
|
|
#else
|
|
struct msgb *msg1 = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_REQ);
|
|
struct msgb *msg2 = l1ctl_msgb_alloc(L1CTL_RACH_REQ);
|
|
struct l1ctl_neigh_pm_req *pm_req = (struct l1ctl_neigh_pm_req *)
|
|
msgb_put(msg1, sizeof(*pm_req));
|
|
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)
|
|
msgb_put(msg2, sizeof(*ul));;
|
|
struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *)
|
|
msgb_put(msg2, sizeof(*rach_req));
|
|
|
|
l1s.tx_power = 0;
|
|
|
|
pm_req->n = 0; /* disable */
|
|
|
|
rach_ra = 0x00;
|
|
rach_req->ra = rach_ra;
|
|
rach_req->offset = 0;
|
|
rach_req->combined = (ccch_conf == 1);
|
|
|
|
l1a_l23_rx(SC_DLCI_L1A_L23, msg1);
|
|
l1a_l23_rx(SC_DLCI_L1A_L23, msg2);
|
|
rach = 1;
|
|
rach_when = jiffies;
|
|
assign = ASSIGN_NONE;
|
|
mode = MODE_RACH;
|
|
refresh_display();
|
|
#endif
|
|
|
|
}
|
|
|
|
static void exit_rach(void)
|
|
{
|
|
rach = 0;
|
|
|
|
request_ul_levels(ul_arfcn);
|
|
|
|
mode = MODE_SYNC;
|
|
refresh_display();
|
|
}
|
|
|
|
static void handle_assign(void)
|
|
{
|
|
if (mode != MODE_RACH)
|
|
return;
|
|
|
|
if (assign == ASSIGN_NONE) {
|
|
unsigned long elapsed = jiffies - rach_when;
|
|
|
|
if (!rach)
|
|
return;
|
|
if (elapsed < HZ * 2)
|
|
return;
|
|
assign = ASSIGN_TIMEOUT;
|
|
rach = 0;
|
|
}
|
|
|
|
refresh_display();
|
|
assign = ASSIGN_NONE;
|
|
}
|
|
|
|
/* Main Program */
|
|
const char *hr = "======================================================================\n";
|
|
|
|
/* match request reference against request history */
|
|
static int gsm48_match_ra(struct gsm48_req_ref *ref)
|
|
{
|
|
uint8_t ia_t1, ia_t2, ia_t3;
|
|
uint8_t cr_t1, cr_t2, cr_t3;
|
|
|
|
if (rach && ref->ra == rach_ra) {
|
|
ia_t1 = ref->t1;
|
|
ia_t2 = ref->t2;
|
|
ia_t3 = (ref->t3_high << 3) | ref->t3_low;
|
|
ref = &rach_ref;
|
|
cr_t1 = ref->t1;
|
|
cr_t2 = ref->t2;
|
|
cr_t3 = (ref->t3_high << 3) | ref->t3_low;
|
|
if (ia_t1 == cr_t1 && ia_t2 == cr_t2 && ia_t3 == cr_t3)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* note: called from IRQ context */
|
|
static void rx_imm_ass(struct msgb *msg)
|
|
{
|
|
struct gsm48_imm_ass *ia = msgb_l3(msg);
|
|
|
|
if (gsm48_match_ra(&ia->req_ref)) {
|
|
assign = ASSIGN_RESULT;
|
|
ta = ia->timing_advance;
|
|
rach = 0;
|
|
}
|
|
}
|
|
|
|
/* note: called from IRQ context */
|
|
static void rx_imm_ass_ext(struct msgb *msg)
|
|
{
|
|
struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
|
|
|
|
if (gsm48_match_ra(&ia->req_ref1)) {
|
|
assign = ASSIGN_RESULT;
|
|
ta = ia->timing_advance1;
|
|
rach = 0;
|
|
}
|
|
if (gsm48_match_ra(&ia->req_ref2)) {
|
|
assign = ASSIGN_RESULT;
|
|
ta = ia->timing_advance2;
|
|
rach = 0;
|
|
}
|
|
}
|
|
|
|
/* note: called from IRQ context */
|
|
static void rx_imm_ass_rej(struct msgb *msg)
|
|
{
|
|
struct gsm48_imm_ass_rej *ia = msgb_l3(msg);
|
|
struct gsm48_req_ref *req_ref;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
/* request reference */
|
|
req_ref = (struct gsm48_req_ref *)
|
|
(((uint8_t *)&ia->req_ref1) + i * 4);
|
|
if (gsm48_match_ra(req_ref)) {
|
|
assign = ASSIGN_REJECT;
|
|
rach = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* note: called from IRQ context */
|
|
static void rx_pch_agch(struct msgb *msg)
|
|
{
|
|
struct gsm48_system_information_type_header *sih;
|
|
|
|
/* store SI */
|
|
sih = msgb_l3(msg);
|
|
switch (sih->system_information) {
|
|
case GSM48_MT_RR_IMM_ASS:
|
|
rx_imm_ass(msg);
|
|
break;
|
|
case GSM48_MT_RR_IMM_ASS_EXT:
|
|
rx_imm_ass_ext(msg);
|
|
break;
|
|
case GSM48_MT_RR_IMM_ASS_REJ:
|
|
rx_imm_ass_rej(msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* note: called from IRQ context */
|
|
static void rx_bcch(struct msgb *msg)
|
|
{
|
|
struct gsm48_system_information_type_header *sih;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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 l1ctl_neigh_pm_ind *pm_ind;
|
|
struct gsm_time tm;
|
|
|
|
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);
|
|
|
|
power = dl->rx_level;
|
|
if (dl->fire_crc >= 2) {
|
|
if (chan_type == RSL_CHAN_BCCH)
|
|
si_new = 0x1ff; /* error frame indication */
|
|
break; /* free, but don't send to sercom */
|
|
}
|
|
|
|
switch (chan_type) {
|
|
case RSL_CHAN_BCCH:
|
|
msg->l3h = msg->l2h;
|
|
rx_bcch(msg);
|
|
break;
|
|
case RSL_CHAN_PCH_AGCH:
|
|
msg->l3h = msg->l2h;
|
|
rx_pch_agch(msg);
|
|
break;
|
|
}
|
|
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;
|
|
case L1CTL_RACH_CONF:
|
|
dl = (struct l1ctl_info_dl *) l1h->data;
|
|
gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
|
|
rach_ref.t1 = tm.t1;
|
|
rach_ref.t2 = tm.t2;
|
|
rach_ref.t3_low = tm.t3 & 0x7;
|
|
rach_ref.t3_high = tm.t3 >> 3;
|
|
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 = HZ * 6 / 10;
|
|
}
|
|
|
|
key_code = code;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
board_init(1);
|
|
|
|
puts("\n\nOsmocomBB 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_assign();
|
|
handle_tone();
|
|
}
|
|
|
|
/* NOT REACHED */
|
|
|
|
twl3025_power_off();
|
|
}
|
|
|