summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-08-18 08:08:48 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2013-10-05 19:47:34 +0200
commit33e625bde27ecbb29a058ccefe6bfa7cb22d04dd (patch)
tree96e44127312254ac790c8009bacfa4b4493e73f1
parent45431603cbebe21b825a5ad1479e8462d8b5ebf0 (diff)
Add EMI application to firmware apps
This application simulates electromagnetic interference caused by a GSM mobile station or a base station. See https://bb.osmocom.org/trac/wiki/emi-firmware for usage.
-rw-r--r--src/target/firmware/Makefile3
-rw-r--r--src/target/firmware/apps/emi/main.c838
-rw-r--r--src/target/firmware/calypso/dsp_extcode.c2
-rw-r--r--src/target/firmware/include/layer1/mframe_sched.h1
-rw-r--r--src/target/firmware/include/layer1/prim.h2
-rw-r--r--src/target/firmware/include/layer1/sync.h10
-rw-r--r--src/target/firmware/layer1/Makefile2
-rw-r--r--src/target/firmware/layer1/mframe_sched.c7
-rw-r--r--src/target/firmware/layer1/prim_emi.c169
9 files changed, 1031 insertions, 3 deletions
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile
index 99a07063..8e8fab31 100644
--- a/src/target/firmware/Makefile
+++ b/src/target/firmware/Makefile
@@ -84,12 +84,13 @@ BOARD_se_j100_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS)
#
# List of all applications (meant to be overridden on command line)
-APPLICATIONS?=hello_world compal_dsp_dump layer1 loader rssi menu trx
+APPLICATIONS?=hello_world compal_dsp_dump layer1 loader rssi menu trx emi
# Applications specific env requirements
APP_loader_ENVIRONMENTS=compalram highram
APP_rssi_ENVIRONMENTS=* -compalram
APP_menu_ENVIRONMENTS=* -highram
+APP_emi_ENVIRONMENTS=* -compalram
# Various objects that are currently linked into all applications
FLASH_OBJS=flash/cfi_flash.o
diff --git a/src/target/firmware/apps/emi/main.c b/src/target/firmware/apps/emi/main.c
new file mode 100644
index 00000000..1a977391
--- /dev/null
+++ b/src/target/firmware/apps/emi/main.c
@@ -0,0 +1,838 @@
+/* EMI Test Transmitter of Free Software for Calypso Phone */
+
+/* (C) 2013 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/apc.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_ARFCN,
+ MODE_TEMPLATE,
+} mode = MODE_MAIN;
+
+static uint8_t transmitter_on = 0;
+static uint16_t arfcn = 0;
+static uint8_t power = 0;
+static uint16_t auxapc_abb;
+static int pcs = 0;
+static int uplink = 1;
+static char input[5];
+static 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},
+};
+
+static 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
+
+#define TONE_JIFFIES ((HZ < 25) ? 1 : HZ / 25)
+static int tone_on = 0;
+
+/* templates */
+
+struct templates {
+ char *name; /* name of template */
+ uint8_t slots; /* number of slots used (multislot) */
+ int burst_num; /* number of bits in burstmap */
+ uint32_t *burst_map;
+};
+
+/* two multiframes of SDCCH */
+static uint32_t template_sdcch[] = {
+ 0x0001e000, 0x0001e000, 0x3c000000, 0x00000000,
+};
+
+/* one multiframe of TCH/F */
+static uint32_t template_tchf[] = {
+ 0xffffff80,
+};
+
+/* one multiframe of TCH/H with SACCH/TF only */
+static uint32_t template_tchf_h_dtx[] = {
+ 0x00080000,
+};
+
+/* one multiframe of TCH/H */
+static uint32_t template_tchh[] = {
+ 0xaaad5500,
+};
+
+/* 32 multiframes of PDCH representing bursts during download (accessing google) */
+static uint32_t template_pdch_ack[] = {
+ 0xffff803c, 0x3c000000, 0x07800001, 0xe00ff7ff, 0xbc01e1e0, 0x0000003c,
+ 0x00000f00, 0x7fbffde0, 0x0f0f0000, 0x0001e000, 0x00000000, 0x000f0000,
+ 0x03c001fe,
+ 0xffff803c, 0x01e00000, 0x00780000, 0x000f0000, 0x03c01fef, 0xff7803c3,
+ 0xc0000000, 0x78000000, 0x0f000003, 0xc01ffeff, 0xf7803fc0, 0x00000078,
+ 0x00001e00,
+ 0xfffff800, 0x01fe0000, 0x0003c000, 0x00f007ff, 0xbffde00f, 0xf0000000,
+ 0x1e000000, 0x07800000, 0x00f00000, 0x3c01feff, 0xf7803c3c, 0x00000007,
+ 0x800001e0,
+ 0x0fffffbc, 0x01e1e000, 0x00003c00, 0x00000780, 0x0001e00f, 0xff7f8000,
+ 0x1fe00000, 0x003c0000, 0x000f0000, 0x3c001eff, 0xf7f8003d, 0xe0000000,
+ 0x780001e0,
+
+};
+
+/* 8 multiframes of PDCH representing bursts during upload (large ping reply) */
+static uint32_t template_pdch[] = {
+ 0xffffffbf, 0xfdffefff, 0x7ffbffdf, 0xfefff7ff, 0xbffdffef, 0xff7ffbff,
+ 0xdffefff7, 0xffbffdff, 0xefff7ffb, 0xffdffeff, 0xf7ffbffd, 0xffefff7f,
+ 0xfbffdffe,
+};
+
+static struct templates templates[] = {
+ { "SDCCH", 1, 102, template_sdcch },
+ { "TCH/F", 1, 26, template_tchf },
+ { "TCH/F (2 TS)", 2, 26, template_tchf },
+ { "TCH/F (3 TS)", 3, 26, template_tchf },
+ { "TCH/F (4 TS)", 4, 26, template_tchf },
+ { "TCH/F (5 TS)", 5, 26, template_tchf },
+ { "TCH/H", 1, 26, template_tchh },
+ { "TCH/F(H) DTX", 1, 26, template_tchf_h_dtx },
+ { "PDCH (ack)", 1, 1664, template_pdch_ack },
+ { "PDCH", 1, 416, template_pdch },
+ { "PDCH (2 TS)", 2, 416, template_pdch },
+ { "PDCH (3 TS)", 3, 416, template_pdch },
+ { "PDCH (4 TS)", 4, 416, template_pdch },
+ { "PDCH (5 TS)", 5, 416, template_pdch },
+ { "RACH", 1, 217, NULL },
+ { 0, 0, 0, NULL }
+};
+
+static int sel_template = 0, scroll_template = 0;
+
+/* UI */
+
+static void refresh_template(void);
+
+static void refresh_display(void)
+{
+ char text[16];
+ int bat = battery_info.battery_percent;
+
+ fb_clear();
+
+ /* header */
+ fb_setbg(FB_COLOR_WHITE);
+ if (1) {
+ fb_setfg(FB_COLOR_BLUE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(0, 7);
+ fb_putstr("Osmocom EMI", -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);
+ if (transmitter_on) {
+ sprintf(text, "%cDEF%c", (power >= 30) ? 'D':'G',
+ (power >= 30) ? 'F':'G');
+ fb_putstr(text, framebuffer->width);
+ } else
+ fb_putstr("GGEGG", 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);
+
+ /* 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);
+ }
+
+ /* select template */
+ if (mode == MODE_TEMPLATE) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (!templates[scroll_template + i].name)
+ break;
+ if (scroll_template + i == cursor) {
+ fb_setfg(FB_COLOR_WHITE);
+ fb_setbg(FB_COLOR_BLUE);
+ }
+ fb_gotoxy(0, 20 + i * 10);
+ fb_putstr(templates[scroll_template + i].name,
+ framebuffer->width);
+ if (scroll_template + i == cursor) {
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ }
+ }
+ }
+
+ /* Frequency / power / template */
+ 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);
+#ifndef CONFIG_TX_ENABLE
+ fb_setfg(FB_COLOR_RED);
+ fb_putstr("TX DISABLED!", framebuffer->width);
+ fb_setfg(FB_COLOR_BLACK);
+#else
+ /* in case of RACH, burst_map is not set. we always display
+ * power. */
+ if (transmitter_on || !templates[sel_template].burst_map) {
+ sprintf(text, "Power %d dBm", power);
+ fb_putstr(text, framebuffer->width);
+ } else
+ fb_putstr("Power -off-", framebuffer->width);
+#endif
+
+ fb_gotoxy(0, 50);
+ fb_putstr(templates[sel_template].name, framebuffer->width);
+ fb_setbg(FB_COLOR_WHITE);
+ }
+
+ /* 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_TEMPLATE)
+ sprintf(text, "%s %s", "back", "enter");
+ else
+ sprintf(text, "%s %s", (pcs) ? "PCS" : "DCS",
+ (uplink || !templates[sel_template].burst_map)
+ ? "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", l1s.emi.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 (!bands[i].max)
+ return;
+ if (check > 1023)
+ return;
+ arfcn = check;
+ band = temp;
+ mode = MODE_MAIN;
+ refresh_display();
+ refresh_template();
+ 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();
+ refresh_template();
+
+ return 0;
+}
+
+static void toggle_dcs_pcs(void)
+{
+ pcs = !pcs;
+ refresh_display();
+ refresh_template();
+}
+
+static void toggle_up_down(void)
+{
+ uplink = !uplink;
+ refresh_display();
+ refresh_template();
+}
+
+static void exit_template(void)
+{
+ mode = MODE_MAIN;
+ refresh_display();
+}
+
+static void enter_template(enum key_codes code)
+{
+ /* enter mode */
+ if (mode != MODE_TEMPLATE) {
+ mode = MODE_TEMPLATE;
+ cursor = sel_template;
+ if (cursor < scroll_template)
+ scroll_template = cursor;
+ else if (cursor >= scroll_template + 4)
+ scroll_template = cursor - 3;
+ refresh_display();
+ return;
+ }
+
+ if (code == KEY_LEFT_SB) {
+ /* back */
+ exit_template();
+ return;
+ }
+
+ if (code == KEY_RIGHT_SB) {
+ sel_template = cursor;
+ mode = MODE_MAIN;
+ refresh_template();
+ refresh_display();
+ return;
+ }
+
+ if (code == KEY_UP) {
+ if (cursor == 0)
+ return;
+ cursor--;
+ if (cursor < scroll_template)
+ scroll_template = cursor;
+ }
+
+ if (code == KEY_DOWN) {
+ if (!templates[cursor + 1].name)
+ return;
+ cursor++;
+ if (cursor >= scroll_template + 4)
+ scroll_template = cursor - 3;
+ }
+
+ refresh_display();
+}
+
+static void tone_inc_dec(int inc)
+{
+ if (inc) {
+ if (l1s.emi.tone + 25 <= 255)
+ l1s.emi.tone += 25;
+ } else {
+ if (l1s.emi.tone - 25 >= 0)
+ l1s.emi.tone -= 25;
+ }
+
+ refresh_display();
+}
+
+static void power_inc_dec(int inc)
+{
+ if (inc) {
+ if (power < 30)
+ power += 2;
+ } else {
+ if (power > 0)
+ power -= 2;
+ }
+
+ refresh_template();
+ /* refresh_template() might change tx power, so we refresh
+ * display afterwards: */
+ refresh_display();
+}
+
+static void toggle_transmitter(void)
+{
+ transmitter_on = !transmitter_on;
+ refresh_display();
+ refresh_template();
+}
+
+static void send_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_pressed_code == KEY_UP
+ || key_pressed_code == KEY_DOWN
+ || key_pressed_code == KEY_STAR
+ || key_pressed_code == KEY_HASH)
+ 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_ARFCN)
+ enter_arfcn(key_code);
+ break;
+ case KEY_UP:
+ if (mode == MODE_MAIN)
+ power_inc_dec(1);
+ else if (mode == MODE_TEMPLATE)
+ enter_template(key_code);
+ break;
+ case KEY_DOWN:
+ if (mode == MODE_MAIN)
+ power_inc_dec(0);
+ else if (mode == MODE_TEMPLATE)
+ enter_template(key_code);
+ 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);
+ else if (mode == MODE_TEMPLATE)
+ exit_template();
+ break;
+ case KEY_RIGHT_SB:
+ if (mode == MODE_MAIN)
+ toggle_up_down();
+ else if (mode == MODE_ARFCN)
+ enter_arfcn(key_code);
+ else if (mode == MODE_TEMPLATE)
+ enter_template(key_code);
+ break;
+ case KEY_OK:
+ if (mode == MODE_MAIN) {
+ if (!templates[sel_template].burst_map) {
+ send_rach();
+ break;
+ }
+ toggle_transmitter();
+ }
+ break;
+ case KEY_MENU:
+ if (mode == MODE_MAIN)
+ enter_template(key_code);
+ break;
+ case KEY_POWER:
+ if (mode == MODE_MAIN) {
+ transmitter_on = 1; /* is turned of by toggeling */
+ toggle_transmitter();
+ } else if (mode == MODE_ARFCN)
+ exit_arfcn();
+ else if (mode == MODE_TEMPLATE)
+ exit_template();
+ break;
+ case KEY_STAR:
+ if (mode == MODE_MAIN)
+ tone_inc_dec(0);
+ break;
+ case KEY_HASH:
+ if (mode == MODE_MAIN)
+ tone_inc_dec(1);
+ break;
+ default:
+ break;
+ }
+
+ key_code = KEY_INV;
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+static void set_tx_power(int dbm)
+{
+ enum gsm_band _band;
+
+ _band = gsm_arfcn2band(arfcn);
+ l1s.tx_power = ms_pwr_ctl_lvl(_band, dbm);
+}
+
+static void send_rach(void)
+{
+ struct msgb *msg = l1ctl_msgb_alloc(L1CTL_RACH_REQ);
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)
+ msgb_put(msg, sizeof(*ul));;
+ struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *)
+ msgb_put(msg, sizeof(*rach_req));
+
+ set_tx_power(power);
+ l1s.serving_cell.arfcn = arfcn;
+
+ rach_req->ra = 0x00;
+ rach_req->offset = 0;
+ rach_req->combined = 0;
+
+ l1a_l23_rx(SC_DLCI_L1A_L23, msg);
+
+ /* make one click */
+ buzzer_volume(l1s.emi.tone);
+ buzzer_note(NOTE(NOTE_C, OCTAVE_5));
+ delay_us(300);
+ buzzer_volume(0);
+}
+
+static void refresh_template(void)
+{
+ mframe_disable(MF_TASK_EMI);
+ buzzer_volume(0);
+
+ if (transmitter_on && templates[sel_template].burst_map) {
+ /* hack: because we cannot control power of multiple burst,
+ * we just force power to 1w, as it is fixed in dsp_extcode
+ */
+ if (templates[sel_template].slots > 1)
+ power = 30;
+ set_tx_power(power);
+ l1s.emi.burst_curr = 0;
+ l1s.emi.burst_map = templates[sel_template].burst_map;
+ l1s.emi.burst_num = templates[sel_template].burst_num;
+ l1s.emi.slots = templates[sel_template].slots;
+ l1s.emi.arfcn = arfcn; /* prim_rach always uses uplink */
+ if (uplink)
+ l1s.emi.arfcn |= ARFCN_UPLINK;
+ mframe_enable(MF_TASK_EMI);
+ }
+}
+
+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;
+}
+
+static void l1a_l23_tx(struct msgb *msg)
+{
+ msgb_free(msg);
+}
+
+
+#include <abb/twl3025.h>
+#define ABB_VAL(reg, val) ( (((reg) & 0x1F) << 1) | (((val) & 0x3FF) << 6) )
+static void calculate_power(void)
+{
+ int auxapc;
+
+ auxapc = apc_tx_dbm2auxapc(GSM_BAND_900, power);
+ auxapc_abb = ABB_VAL(AUXAPC, auxapc);
+ printf("Power AUXAPC value for %ddBm = %d (0x%04x)\n", power, auxapc,
+ auxapc_abb);
+}
+
+int main(void)
+{
+ int i;
+
+ board_init(1);
+
+ puts("\n\nOsmocomBB EMI Test Transmitter (revision " GIT_REVISION
+ ")\n");
+ puts(hr);
+
+ calculate_power();
+
+ /* 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 (display/template) */
+ inc_dec_arfcn(1);
+
+ while (1) {
+ for (i = 0; i < 256; i++) {
+ l1a_compl_execute();
+ osmo_timers_update();
+ handle_key_code();
+ l1a_l23_handler();
+ }
+ refresh_display();
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+
diff --git a/src/target/firmware/calypso/dsp_extcode.c b/src/target/firmware/calypso/dsp_extcode.c
index 585769b4..90c15e11 100644
--- a/src/target/firmware/calypso/dsp_extcode.c
+++ b/src/target/firmware/calypso/dsp_extcode.c
@@ -172,7 +172,7 @@ static const struct dsp_section dsp_extcode[] = {
0x0654, 0x7692, 0x07d4, 0x7692,
0x0794, 0x7692, 0x03d4, 0x7692,
0x0014, 0x7692, 0x0014, 0x7692,
- 0x31d2, 0xf074, 0xb55f, 0xfc00,
+ 0x7212, 0xf074, 0xb55f, 0xfc00,
0x76f8, 0x2402, 0x2000, 0x61f8,
0x3fb0, 0x0001, 0xf820, 0x03ff,
0x76f8, 0x2402, 0x2200, 0xfc00
diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h
index 7494eebc..9f1a4a91 100644
--- a/src/target/firmware/include/layer1/mframe_sched.h
+++ b/src/target/firmware/include/layer1/mframe_sched.h
@@ -37,6 +37,7 @@ enum mframe_task {
MF_TASK_NEIGH_PM26O,
MF_TASK_BTS,
+ MF_TASK_EMI,
/* Test task: send Normal Burst in all timeslots */
MF_TASK_UL_ALL_NB,
diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h
index 654daa2f..77c81814 100644
--- a/src/target/firmware/include/layer1/prim.h
+++ b/src/target/firmware/include/layer1/prim.h
@@ -33,4 +33,6 @@ extern const struct tdma_sched_item neigh_pm_sched_set[];
extern const struct tdma_sched_item bts_sched_set[];
+extern const struct tdma_sched_item emi_sched_set[];
+
#endif /* _L1_PRIM_H */
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
index 2cb5152c..418b6d99 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -160,6 +160,16 @@ struct l1s_state {
uint16_t arfcn;
uint8_t bsic;
} bts;
+
+ /* emi mode */
+ struct {
+ uint16_t arfcn;
+ uint8_t slots;
+ uint32_t *burst_map;
+ int32_t burst_num;
+ int32_t burst_curr;
+ uint8_t tone;
+ } emi;
};
extern struct l1s_state l1s;
diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile
index 8e655b67..6c8a2c01 100644
--- a/src/target/firmware/layer1/Makefile
+++ b/src/target/firmware/layer1/Makefile
@@ -6,5 +6,5 @@ LIB_layer1_SRCS=avg.c agc.c afc.c toa.c sync.c tdma_sched.c tpu_window.c init.c
trx_dummy.c
LIB_layer1_SRCS += prim_pm.c prim_rach.c prim_tx_nb.c prim_rx_nb.c prim_fbsb.c \
- prim_freq.c prim_utils.c prim_tch.c prim_bts.c
+ prim_freq.c prim_utils.c prim_tch.c prim_bts.c prim_emi.c
diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c
index c55b809b..107c22e0 100644
--- a/src/target/firmware/layer1/mframe_sched.c
+++ b/src/target/firmware/layer1/mframe_sched.c
@@ -316,6 +316,12 @@ static const struct mframe_sched_item mf_bts[] = {
{ .sched_set = NULL }
};
+/* EMI */
+static const struct mframe_sched_item mf_emi[] = {
+ { .sched_set = emi_sched_set, .modulo = 1, .frame_nr = 0 },
+ { .sched_set = NULL }
+};
+
/* Test TX */
static const struct mframe_sched_item mf_tx_all_nb[] = {
{ .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 },
@@ -356,6 +362,7 @@ static const struct mframe_sched_item *sched_set_for_task[32] = {
[MF_TASK_NEIGH_PM26O] = mf_neigh_pm26_odd,
[MF_TASK_BTS] = mf_bts,
+ [MF_TASK_EMI] = mf_emi,
[MF_TASK_UL_ALL_NB] = mf_tx_all_nb,
};
diff --git a/src/target/firmware/layer1/prim_emi.c b/src/target/firmware/layer1/prim_emi.c
new file mode 100644
index 00000000..3b299faa
--- /dev/null
+++ b/src/target/firmware/layer1/prim_emi.c
@@ -0,0 +1,169 @@
+/* Layer 1 - EMI functions */
+
+/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * based on code by Sylvain Munaut <tnt@246tnt.com>
+ *
+ * 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 <byteorder.h>
+
+#include <calypso/dsp.h>
+#include <calypso/buzzer.h>
+
+#include <layer1/l23_api.h>
+#include <layer1/sync.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/trx.h>
+
+
+/* ------------------------------------------------------------------------ */
+/* DSP extensions API */
+/* ------------------------------------------------------------------------ */
+
+#define BASE_API 0xFFD00000
+
+#define API_DSP2ARM(x) (BASE_API + ((x) - 0x0800) * sizeof(uint16_t))
+
+#define BASE_API_EXT_DB0 API_DSP2ARM(0x2000)
+#define BASE_API_EXT_DB1 API_DSP2ARM(0x2200)
+#define BASE_API_EXT_NDB API_DSP2ARM(0x2400)
+
+struct dsp_ext_ndb {
+ uint16_t active;
+ uint16_t tsc;
+} __attribute__((packed));
+
+struct dsp_ext_db {
+ /* TX command and data ptr */
+ struct {
+#define DSP_EXT_TX_CMD_NONE 0
+#define DSP_EXT_TX_CMD_FB 1
+#define DSP_EXT_TX_CMD_SB 2
+#define DSP_EXT_TX_CMD_DUMMY 3
+#define DSP_EXT_TX_CMD_AB 4
+#define DSP_EXT_TX_CMD_NB 5
+ uint16_t cmd;
+ uint16_t data;
+ } tx[8];
+
+ /* RX command and data ptr */
+ struct {
+#define DSP_EXT_RX_CMD_NONE 0
+#define DSP_EXT_RX_CMD_AB 1
+#define DSP_EXT_RX_CMD_NB 2
+ uint16_t cmd;
+ uint16_t data;
+ } rx[8];
+
+ /* SCH data */
+ uint16_t sch[2];
+
+ /* Generic data array */
+ uint16_t data[0];
+} __attribute__((packed));
+
+struct dsp_ext_api {
+ struct dsp_ext_ndb *ndb;
+ struct dsp_ext_db *db[2];
+};
+
+struct dsp_ext_api dsp_ext_api_emi = {
+ .ndb = (struct dsp_ext_ndb *) BASE_API_EXT_NDB,
+ .db = {
+ (struct dsp_ext_db *) BASE_API_EXT_DB0,
+ (struct dsp_ext_db *) BASE_API_EXT_DB1,
+ },
+};
+
+static inline struct dsp_ext_db *
+dsp_ext_get_db(int r_wn /* 0=W, 1=R */)
+{
+ int idx = r_wn ? dsp_api.r_page : dsp_api.w_page;
+ return dsp_ext_api_emi.db[idx];
+}
+
+static int
+l1s_emi_resp(uint8_t p1, uint8_t p2, uint16_t p3)
+{
+ /* We're done with this */
+ dsp_api.r_page_used = 1;
+
+ return 0;
+}
+
+static int
+l1s_emi_cmd(uint8_t p1, uint8_t p2, uint16_t p3)
+{
+ struct dsp_ext_db *db = dsp_ext_get_db(0);
+
+ int i;
+
+ /* Enable extensions */
+ dsp_ext_api_emi.ndb->active = 1;
+
+
+ /* skip if not burst to be sent */
+ l1s.emi.burst_curr++;
+ if (l1s.emi.burst_curr == l1s.emi.burst_num)
+ l1s.emi.burst_curr = 0;
+ if (!(l1s.emi.burst_map[l1s.emi.burst_curr >> 5] & (1 << (31 - (l1s.emi.burst_curr & 0x1f)))))
+ return 0;
+ buzzer_volume(l1s.emi.tone);
+ buzzer_note(NOTE(NOTE_C, OCTAVE_5));
+
+ /* RX side */
+ /* ------- */
+
+ db->rx[0].cmd = 0;
+ db->rx[0].data = 0x000;
+
+
+ /* TX side */
+ /* ------- */
+
+ #define SLOT 3
+
+ /* Reset all commands to dummy burst */
+ for (i=0; i<8; i++)
+ db->tx[i].cmd = DSP_EXT_TX_CMD_DUMMY;
+
+ /* Enable the task */
+ dsp_api.db_w->d_task_ra = RACH_DSP_TASK;
+
+ /* Open TX window */
+ l1s_tx_apc_helper(l1s.emi.arfcn);
+ l1s_tx_multi_win_ctrl(l1s.emi.arfcn, 0, 2, l1s.emi.slots);
+
+ /* delay some time */
+ delay_us(300);
+ buzzer_volume(0);
+
+ return 0;
+}
+
+const struct tdma_sched_item emi_sched_set[] = {
+ SCHED_ITEM_DT(l1s_emi_cmd, 2, 0, 0), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_emi_resp, -5, 0, 0), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};