diff options
Diffstat (limited to 'src/target/firmware/apps')
-rw-r--r-- | src/target/firmware/apps/chainload/main.c | 54 | ||||
-rw-r--r-- | src/target/firmware/apps/compal_dsp_dump/main.c | 85 | ||||
-rw-r--r-- | src/target/firmware/apps/hello_world/main.c | 199 | ||||
-rw-r--r-- | src/target/firmware/apps/layer1/main.c | 173 | ||||
-rw-r--r-- | src/target/firmware/apps/loader/main.c | 445 | ||||
-rw-r--r-- | src/target/firmware/apps/loader/protocol.h | 37 | ||||
-rw-r--r-- | src/target/firmware/apps/loader_mtk/main.c | 366 | ||||
-rw-r--r-- | src/target/firmware/apps/rssi/main.c | 1562 | ||||
-rwxr-xr-x | src/target/firmware/apps/simtest/main.c | 362 |
9 files changed, 3283 insertions, 0 deletions
diff --git a/src/target/firmware/apps/chainload/main.c b/src/target/firmware/apps/chainload/main.c new file mode 100644 index 00000000..9dfa2176 --- /dev/null +++ b/src/target/firmware/apps/chainload/main.c @@ -0,0 +1,54 @@ +/* Compal ramloader -> Calypso romloader Chainloading application */ + +/* (C) 2010 by Steve Markgraf <steve@steve-m.de> + * + * 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 <debug.h> +#include <memory.h> +#include <delay.h> + +#include <calypso/clock.h> +#include <calypso/timer.h> + +/* Main Program */ + +static void device_enter_loader(unsigned char bootrom) { + calypso_bootrom(bootrom); + void (*entry)( void ) = (void (*)(void))0; + entry(); +} + +int main(void) +{ + /* Always disable wdt (some platforms enable it on boot) */ + wdog_enable(0); + + /* enable Calypso romloader mapping and jump there */ + delay_ms(200); + device_enter_loader(1); + + /* Not reached */ + while(1) { + } +} diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c new file mode 100644 index 00000000..ba48790f --- /dev/null +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -0,0 +1,85 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 Harald Welte <laforge@gnumonks.org> + * (C) 2010 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 <memory.h> +#include <delay.h> +#include <stdio.h> +#include <stdint.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 <comm/timer.h> +#include <fb/framebuffer.h> + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + board_init(); + + puts("\n\nOsmocomBB Compal DSP Dumper (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + fb_clear(); + + fb_setfg(FB_COLOR_BLACK); + fb_setbg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVB14); + + fb_gotoxy(2,20); + fb_putstr("DSP Dump",framebuffer->width-4); + + fb_setfg(FB_COLOR_RED); + fb_setbg(FB_COLOR_BLUE); + + fb_gotoxy(2,25); + fb_boxto(framebuffer->width-3,38); + + fb_setfg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(8,33); + fb_putstr("osmocom-bb",framebuffer->width-4); + + fb_flush(); + + /* Dump DSP content */ + dsp_dump(); + + while (1) { + osmo_timers_update(); + } +} + diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c new file mode 100644 index 00000000..02cab050 --- /dev/null +++ b/src/target/firmware/apps/hello_world/main.c @@ -0,0 +1,199 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * 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 <debug.h> +#include <memory.h> +#include <delay.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 <comm/sercomm.h> +#include <comm/timer.h> +#include <fb/framebuffer.h> +#include <battery/battery.h> + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +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; + puts("l1a_l23_rx_cb: "); + for (i = 0; i < msg->len; i++) + printf("%02x ", msg->data[i]); + puts("\n"); +} + +void +write_battery_info(void *p){ + char buf[128]; + + fb_setfg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_C64); + + snprintf(buf,sizeof(buf),"B: %04d mV",battery_info.bat_volt_mV); + fb_gotoxy(8,41); + fb_putstr(buf,framebuffer->width-8); + + snprintf(buf,sizeof(buf),"C: %04d mV",battery_info.charger_volt_mV); + fb_gotoxy(8,49); + fb_putstr(buf,framebuffer->width-8); + + snprintf(buf,sizeof(buf),"F: %08x",battery_info.flags); + fb_gotoxy(8,57); + fb_putstr(buf,framebuffer->width-8); + + fb_flush(); + osmo_timer_schedule((struct osmo_timer_list*)p,100); + +} + +/* timer that fires the charging loop regularly */ +static struct osmo_timer_list write_battery_info_timer = { + .cb = &write_battery_info, + .data = &write_battery_info_timer +}; + +int main(void) +{ + board_init(); + + puts("\n\nOsmocomBB Hello World (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); + + fb_clear(); + + fb_setfg(FB_COLOR_BLACK); + fb_setbg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVB14); + + fb_gotoxy(2,20); + fb_putstr("Hello World!",framebuffer->width-4); + + fb_setfg(FB_COLOR_RED); + fb_setbg(FB_COLOR_BLUE); + + fb_gotoxy(2,25); + fb_boxto(framebuffer->width-3,38); + + fb_setfg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(8,33); + fb_putstr("osmocom-bb",framebuffer->width-4); + + fb_flush(); + + + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb); + + osmo_timer_schedule(&write_battery_info_timer,100); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + osmo_timers_update(); + } + + twl3025_power_off(); + + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + char test[16]; + + if (state != PRESSED) + return; + + switch (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: + // used to be display_puts... + break; + case KEY_STAR: + // used to be display puts... + break; + case KEY_HASH: + // used to be display puts... + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c new file mode 100644 index 00000000..6a78bef8 --- /dev/null +++ b/src/target/firmware/apps/layer1/main.c @@ -0,0 +1,173 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * 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 <debug.h> +#include <memory.h> +#include <string.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <calypso/sim.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <fb/framebuffer.h> + +const char *hr = "======================================================================\n"; + +/* MAIN program **************************************************************/ + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + uint8_t atr[20]; + uint8_t atrLength = 0; + + board_init(); + + puts("\n\nOsmocomBB Layer 1 (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config after PLL set */ + calypso_clk_dump(); + puts(hr); + + fb_clear(); + + fb_setfg(FB_COLOR_BLACK); + fb_setbg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVB14); + + fb_gotoxy(2,20); + fb_putstr("Layer 1",framebuffer->width-4); + + fb_setfg(FB_COLOR_RED); + fb_setbg(FB_COLOR_BLUE); + + fb_gotoxy(2,25); + fb_boxto(framebuffer->width-3,38); + + fb_setfg(FB_COLOR_WHITE); + fb_setfont(FB_FONT_HELVR08); + fb_gotoxy(8,33); + fb_putstr("osmocom-bb",framebuffer->width-4); + + fb_flush(); + + /* initialize SIM */ + calypso_sim_init(); + + puts("Power up simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_powerup(atr); + + layer1_init(); + + tpu_frame_irq_en(1, 1); + + while (1) { + l1a_compl_execute(); + osmo_timers_update(); + sim_handler(); + l1a_l23_handler(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int afcout = 0; + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } + /* power down SIM, TODO: this will happen with every key pressed, + put it somewhere else ! */ + calypso_sim_powerdown(); +} + + diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c new file mode 100644 index 00000000..50a39ddd --- /dev/null +++ b/src/target/firmware/apps/loader/main.c @@ -0,0 +1,445 @@ +/* boot loader for Calypso phones */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * 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 <debug.h> +#include <memory.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <console.h> +#include <manifest.h> + +#include <osmocom/core/crc16.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <calypso/backlight.h> +#include <uart.h> +#include <calypso/timer.h> + +#include <flash/cfi_flash.h> + +#include "protocol.h" + +/* Main Program */ +const char *hr = + "======================================================================\n"; + +static void key_handler(enum key_codes code, enum key_states state); +static void cmd_handler(uint8_t dlci, struct msgb *msg); + +int flag = 0; + +static void flush_uart(void) +{ + unsigned i; + for (i = 0; i < 500; i++) { + uart_poll(SERCOMM_UART_NR); + delay_ms(1); + } +} + +static void device_poweroff(void) +{ + flush_uart(); + twl3025_power_off(); +} + +static void device_reset(void) +{ + flush_uart(); + wdog_reset(); +} + +static void device_enter_loader(unsigned char bootrom) +{ + flush_uart(); + + calypso_bootrom(bootrom); + void (*entry) (void) = (void (*)(void))0; + entry(); +} + +static void device_jump(void *entry) +{ + flush_uart(); + + void (*f) (void) = (void (*)(void))entry; + f(); +} + +static void loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) +{ + msgb_put_u8(msg, command); + sercomm_sendmsg(dlci, msg); +} + +extern unsigned char _start; + +static void loader_send_init(uint8_t dlci) +{ + struct msgb *msg = sercomm_alloc_msgb(9); + msgb_put_u8(msg, LOADER_INIT); + msgb_put_u32(msg, 0); + msgb_put_u32(msg, &_start); + sercomm_sendmsg(dlci, msg); +} + +flash_t the_flash; + +extern void putchar_asm(uint32_t c); + +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; + +int main(void) +{ + /* Simulate a compal loader saying "ACK" */ + int i = 0; + for (i = 0; i < sizeof(phone_ack); i++) { + putchar_asm(phone_ack[i]); + } + + /* Always disable wdt (some platforms enable it on boot) */ + wdog_enable(0); + + /* Disable the bootrom mapping */ + calypso_bootrom(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Backlight */ + bl_mode_pwl(1); + bl_level(50); + + /* Initialize UART without interrupts */ + uart_init(SERCOMM_UART_NR, 0); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* Initialize HDLC subsystem */ + sercomm_init(); + + /* Say hi */ + puts("\n\nOsmocomBB Loader (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Identify environment */ + printf("Running on %s in environment %s\n", manifest_board, + manifest_environment); + + /* Initialize flash driver */ + if (flash_init(&the_flash, 0)) { + puts("Failed to initialize flash!\n"); + } else { + printf("Found flash of %d bytes at 0x%x with %d regions\n", + the_flash.f_size, the_flash.f_base, + the_flash.f_nregions); + + int i; + for (i = 0; i < the_flash.f_nregions; i++) { + printf(" Region %d of %d pages with %d bytes each.\n", + i, + the_flash.f_regions[i].fr_bnum, + the_flash.f_regions[i].fr_bsize); + } + + } + + /* Set up a key handler for powering off */ + keypad_set_handler(&key_handler); + + /* Set up loader communications */ + sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler); + + /* Notify any running osmoload about our startup */ + loader_send_init(SC_DLCI_LOADER); + + /* Wait for events */ + while (1) { + keypad_poll(); + uart_poll(SERCOMM_UART_NR); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static void cmd_handler(uint8_t dlci, struct msgb *msg) +{ + if (msg->data_len < 1) { + return; + } + + uint8_t command = msgb_pull_u8(msg); + + int res; + + flash_lock_t lock; + + void *data; + + uint8_t chip; + uint8_t nbytes; + uint16_t crc, mycrc; + uint32_t address; + + struct msgb *reply = sercomm_alloc_msgb(256); // XXX + + if (!reply) { + printf("Failed to allocate reply buffer!\n"); + goto out; + } + + switch (command) { + + case LOADER_PING: + loader_send_simple(reply, dlci, LOADER_PING); + break; + + case LOADER_RESET: + loader_send_simple(reply, dlci, LOADER_RESET); + device_reset(); + break; + + case LOADER_POWEROFF: + loader_send_simple(reply, dlci, LOADER_POWEROFF); + device_poweroff(); + break; + + case LOADER_ENTER_ROM_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER); + device_enter_loader(1); + break; + + case LOADER_ENTER_FLASH_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER); + device_enter_loader(0); + break; + + case LOADER_MEM_READ: + + nbytes = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + crc = osmo_crc16(0, (void *)address, nbytes); + + msgb_put_u8(reply, LOADER_MEM_READ); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, crc); + msgb_put_u32(reply, address); + + memcpy(msgb_put(reply, nbytes), (void *)address, nbytes); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_MEM_WRITE: + + nbytes = msgb_pull_u8(msg); + crc = msgb_pull_u16(msg); + address = msgb_pull_u32(msg); + + data = msgb_pull(msg, nbytes); + + mycrc = osmo_crc16(0, data, nbytes); + + if (mycrc == crc) { + memcpy((void *)address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_MEM_WRITE); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_JUMP: + + address = msgb_pull_u32(msg); + + msgb_put_u8(reply, LOADER_JUMP); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + device_jump((void *)address); + + break; + + case LOADER_FLASH_INFO: + + msgb_put_u8(reply, LOADER_FLASH_INFO); + msgb_put_u8(reply, 1); // nchips + + // chip 1 + msgb_put_u32(reply, the_flash.f_base); + msgb_put_u32(reply, the_flash.f_size); + msgb_put_u8(reply, the_flash.f_nregions); + + int i; + for (i = 0; i < the_flash.f_nregions; i++) { + msgb_put_u32(reply, the_flash.f_regions[i].fr_bnum); + msgb_put_u32(reply, the_flash.f_regions[i].fr_bsize); + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_ERASE: + case LOADER_FLASH_UNLOCK: + case LOADER_FLASH_LOCK: + case LOADER_FLASH_LOCKDOWN: + + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + if (command == LOADER_FLASH_ERASE) { + res = flash_block_erase(&the_flash, address); + } + if (command == LOADER_FLASH_UNLOCK) { + res = flash_block_unlock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCK) { + res = flash_block_lock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCKDOWN) { + res = flash_block_lockdown(&the_flash, address); + } + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + msgb_put_u32(reply, (res != 0)); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_GETLOCK: + + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + lock = flash_block_getlock(&the_flash, address); + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + switch (lock) { + case FLASH_UNLOCKED: + msgb_put_u32(reply, LOADER_FLASH_UNLOCKED); + break; + case FLASH_LOCKED: + msgb_put_u32(reply, LOADER_FLASH_LOCKED); + break; + case FLASH_LOCKED_DOWN: + msgb_put_u32(reply, LOADER_FLASH_LOCKED_DOWN); + break; + default: + msgb_put_u32(reply, 0xFFFFFFFF); + break; + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_PROGRAM: + + nbytes = msgb_pull_u8(msg); + crc = msgb_pull_u16(msg); + msgb_pull_u8(msg); // XXX align + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + data = msgb_pull(msg, nbytes); + + mycrc = osmo_crc16(0, data, nbytes); + + if (mycrc == crc) { + res = flash_program(&the_flash, address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_FLASH_PROGRAM); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u8(reply, 0); // XXX align + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + msgb_put_u32(reply, (uint32_t) res); // XXX + + sercomm_sendmsg(dlci, reply); + + break; + + default: + printf("unknown command %d\n", command); + + msgb_free(reply); + + break; + } + + out: + + msgb_free(msg); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_POWER: + puts("Powering off due to keypress.\n"); + device_poweroff(); + break; + case KEY_OK: + puts("Resetting due to keypress.\n"); + device_reset(); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/loader/protocol.h b/src/target/firmware/apps/loader/protocol.h new file mode 100644 index 00000000..0a61c89e --- /dev/null +++ b/src/target/firmware/apps/loader/protocol.h @@ -0,0 +1,37 @@ + +enum loader_command { + /* init message from loader */ + LOADER_INIT, + + /* ping / pong */ + LOADER_PING, + + /* lifecycle requests */ + LOADER_RESET, + LOADER_POWEROFF, + + /* jumps */ + LOADER_JUMP, + LOADER_ENTER_ROM_LOADER, + LOADER_ENTER_FLASH_LOADER, + + /* generic memory ops */ + LOADER_MEM_READ, + LOADER_MEM_WRITE, + + /* flash operations */ + LOADER_FLASH_INFO, + LOADER_FLASH_ERASE, + LOADER_FLASH_UNLOCK, + LOADER_FLASH_LOCK, + LOADER_FLASH_LOCKDOWN, + LOADER_FLASH_GETLOCK, + LOADER_FLASH_PROGRAM, + +}; + +enum loader_flash_lock { + LOADER_FLASH_UNLOCKED = 0, + LOADER_FLASH_LOCKED, + LOADER_FLASH_LOCKED_DOWN, +}; diff --git a/src/target/firmware/apps/loader_mtk/main.c b/src/target/firmware/apps/loader_mtk/main.c new file mode 100644 index 00000000..7748dc45 --- /dev/null +++ b/src/target/firmware/apps/loader_mtk/main.c @@ -0,0 +1,366 @@ +/* + * boot loader for MTK phones (based on the calypso-version) + * + * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * (C) 2011 by Wolfram Sang <wolfram@the-dreams.de> + * + * 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 <debug.h> +#include <memory.h> +#include <delay.h> +#include <keypad.h> +#include <board.h> +#include <console.h> +#include <defines.h> +#include <manifest.h> + +#include <osmocom/core/crc16.h> + +#include <comm/sercomm.h> + +#include <uart.h> + +#include <flash/cfi_flash.h> + +#include <mtk/emi.h> +#include <mtk/mt6235.h> +#include <mtk/system.h> + +#include "../loader/protocol.h" + +/* Main Program */ +const char *hr = + "======================================================================\n"; + +static void cmd_handler(uint8_t dlci, struct msgb *msg); + +int flag = 0; + +static void flush_uart(void) +{ + unsigned i; + for (i = 0; i < 500; i++) { + uart_poll(SERCOMM_UART_NR); + delay_ms(1); + } +} + +static void device_poweroff(void) +{ + flush_uart(); + writew(BBPU_MAGIC | RTC_BBPU_WRITE_EN, + MTK_RTC_BBPU); + writew(1, MTK_RTC_WRTGR); +} + +static void device_reset(void) +{ + flush_uart(); +} + +static void device_enter_loader(__unused unsigned char bootrom) +{ + flush_uart(); + delay_ms(2000); + void (*entry)( void ) = (void (*)(void))0; + entry(); +} + +static void device_jump(void *entry) +{ + flush_uart(); + + void (*f) (void) = (void (*)(void))entry; + f(); +} + +static void loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) +{ + msgb_put_u8(msg, command); + sercomm_sendmsg(dlci, msg); +} + +extern unsigned char _start; + +flash_t the_flash; + +extern void putchar_asm(uint32_t c); + +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; + +int main(void) +{ + board_init (); + + /* Initialize HDLC subsystem */ + sercomm_init(); + + /* Say hi */ + puts("\n\nOsmocomBB Loader (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Identify environment */ + printf("\nRunning on %s in environment %s\n", manifest_board, + manifest_environment); + + printf("\nHW_CODE = 0x%04x", readw(MTK_CONFG_HW_CODE)); + + /* Set up loader communications */ + sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler); + + /* Wait for events */ + + while (1) { + uart_poll(SERCOMM_UART_NR); + } + +} + +static void cmd_handler(uint8_t dlci, struct msgb *msg) +{ + if (msg->data_len < 1) { + return; + } + + uint8_t command = msgb_pull_u8(msg); + + int res; + + flash_lock_t lock; + + void *data; + + uint8_t chip; + uint8_t nbytes; + uint16_t crc, mycrc; + uint32_t address; + + struct msgb *reply = sercomm_alloc_msgb(256); // XXX + + if (!reply) { + printf("Failed to allocate reply buffer!\n"); + goto out; + } + + switch (command) { + + case LOADER_PING: + loader_send_simple(reply, dlci, LOADER_PING); + break; + + case LOADER_RESET: + loader_send_simple(reply, dlci, LOADER_RESET); + device_reset(); + break; + + case LOADER_POWEROFF: + loader_send_simple(reply, dlci, LOADER_POWEROFF); + device_poweroff(); + break; + + case LOADER_ENTER_ROM_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER); + device_enter_loader(1); + break; + + case LOADER_ENTER_FLASH_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER); + device_enter_loader(0); + break; + + case LOADER_MEM_READ: + + nbytes = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + crc = osmo_crc16(0, (void *)address, nbytes); + + msgb_put_u8(reply, LOADER_MEM_READ); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, crc); + msgb_put_u32(reply, address); + + memcpy(msgb_put(reply, nbytes), (void *)address, nbytes); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_MEM_WRITE: + + nbytes = msgb_pull_u8(msg); + crc = msgb_pull_u16(msg); + address = msgb_pull_u32(msg); + + data = msgb_pull(msg, nbytes); + + mycrc = osmo_crc16(0, data, nbytes); + + if (mycrc == crc) { + memcpy((void *)address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_MEM_WRITE); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_JUMP: + + address = msgb_pull_u32(msg); + + msgb_put_u8(reply, LOADER_JUMP); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + device_jump((void *)address); + + break; + + case LOADER_FLASH_INFO: + + msgb_put_u8(reply, LOADER_FLASH_INFO); + msgb_put_u8(reply, 1); // nchips + + // chip 1 + msgb_put_u32(reply, the_flash.f_base); + msgb_put_u32(reply, the_flash.f_size); + msgb_put_u8(reply, the_flash.f_nregions); + + unsigned i; + for (i = 0; i < the_flash.f_nregions; i++) { + msgb_put_u32(reply, the_flash.f_regions[i].fr_bnum); + msgb_put_u32(reply, the_flash.f_regions[i].fr_bsize); + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_ERASE: + case LOADER_FLASH_UNLOCK: + case LOADER_FLASH_LOCK: + case LOADER_FLASH_LOCKDOWN: + + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + if (command == LOADER_FLASH_ERASE) { + res = flash_block_erase(&the_flash, address); + } + if (command == LOADER_FLASH_UNLOCK) { + res = flash_block_unlock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCK) { + res = flash_block_lock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCKDOWN) { + res = flash_block_lockdown(&the_flash, address); + } + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + msgb_put_u32(reply, (res != 0)); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_GETLOCK: + + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + lock = flash_block_getlock(&the_flash, address); + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + switch (lock) { + case FLASH_UNLOCKED: + msgb_put_u32(reply, LOADER_FLASH_UNLOCKED); + break; + case FLASH_LOCKED: + msgb_put_u32(reply, LOADER_FLASH_LOCKED); + break; + case FLASH_LOCKED_DOWN: + msgb_put_u32(reply, LOADER_FLASH_LOCKED_DOWN); + break; + default: + msgb_put_u32(reply, 0xFFFFFFFF); + break; + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_PROGRAM: + + nbytes = msgb_pull_u8(msg); + crc = msgb_pull_u16(msg); + msgb_pull_u8(msg); // XXX align + chip = msgb_pull_u8(msg); + address = msgb_pull_u32(msg); + + data = msgb_pull(msg, nbytes); + + mycrc = osmo_crc16(0, data, nbytes); + + if (mycrc == crc) { + res = flash_program(&the_flash, address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_FLASH_PROGRAM); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u8(reply, 0); // XXX align + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + msgb_put_u32(reply, (uint32_t) res); // XXX + + sercomm_sendmsg(dlci, reply); + + break; + + default: + printf("unknown command %d\n", command); + + msgb_free(reply); + + break; + } + + out: + + msgb_free(msg); +} diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c new file mode 100644 index 00000000..b2cafae4 --- /dev/null +++ b/src/target/firmware/apps/rssi/main.c @@ -0,0 +1,1562 @@ +/* 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 agains 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(); + + 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(); +} + diff --git a/src/target/firmware/apps/simtest/main.c b/src/target/firmware/apps/simtest/main.c new file mode 100755 index 00000000..8e089d32 --- /dev/null +++ b/src/target/firmware/apps/simtest/main.c @@ -0,0 +1,362 @@ +/* SIM test application */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * 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 <debug.h> +#include <memory.h> +#include <delay.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 <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/sim.h> + +#define DEBUG + +/* Dump bytes in hex on the console */ +static void myHexdump(uint8_t *data, int len) +{ + int i; + + for(i=0;i<len;i++) + printf("%02x ",data[i]); + + printf("(%i bytes)\n", len); + + return; +} + +/* SIM instructions + All instructions a standard sim card must feature: */ +#define SIM_CLASS 0xA0 /* Class that contains the following instructions */ +#define SIM_SELECT 0xA4 /* Select a file on the card */ +#define SIM_STATUS 0xF2 /* Get the status of the currently selected file */ +#define SIM_READ_BINARY 0xB0 /* Read file in binary mode */ +#define SIM_UPDATE_BINARY 0xD6 /* Write file in binary mode */ +#define SIM_READ_RECORD 0xB2 /* Read record of a record based file */ +#define SIM_UPDATE_RECORD 0xDC /* Write record of a record based file */ +#define SIM_SEEK 0xA2 /* Seek in a record based file */ +#define SIM_INCREASE 0x32 /* Increase a record in a record based file */ +#define SIM_VERIFY_CHV 0x20 /* Authenticate with card (enter pin) */ +#define SIM_CHANGE_CHV 0x24 /* Change pin */ +#define SIM_DISABLE_CHV 0x26 /* Disable pin so that no authentication is needed anymore */ +#define SIM_ENABLE_CHV 0x28 /* Enable pin, authentication is now needed again */ +#define SIM_UNBLOCK_CHV 0x2C /* Unblock pin when it is blocked by entering a wrong pin three times */ +#define SIM_INVALIDATE 0x04 /* Invalidate the current elementry file (file in a subdirectory) */ +#define SIM_REHABILITATE 0x44 /* Rehabilitate the current elementry file (file in a subdirectory) */ +#define SIM_RUN_GSM_ALGORITHM 0x88 /* Run the GSM A3 authentication algorithm in the card */ +#define SIM_SLEEP 0xFA /* Sleep command (only used in Phase 1 GSM) */ +#define SIM_GET_RESPONSE 0xC0 /* Get the response of a command from the card */ + +/* File identifiers (filenames) + The file identifiers are the standardized file identifiers mentioned in the + GSM-11-11 specification. */ +#define SIM_MF 0x3F00 +#define SIM_EF_ICCID 0x2FE2 +#define SIM_DF_TELECOM 0x7F10 +#define SIM_EF_ADN 0x6F3A +#define SIM_EF_FDN 0x6F3B +#define SIM_EF_SMS 0x6F3C +#define SIM_EF_CCP 0x6F3D +#define SIM_EF_MSISDN 0x6F40 +#define SIM_EF_SMSP 0x6F42 +#define SIM_EF_SMSS 0x6F43 +#define SIM_EF_LND 0x6F44 +#define SIM_EF_EXT1 0x6F4A +#define SIM_EF_EXT2 0x6F4B +#define SIM_DF_GSM 0x7F20 +#define SIM_EF_LP 0x6F05 +#define SIM_EF_IMSI 0x6F07 +#define SIM_EF_KC 0x6F20 +#define SIM_EF_PLMNsel 0x6F30 +#define SIM_EF_HPLMN 0x6F31 +#define SIM_EF_ACMmax 0x6F37 +#define SIM_EF_SST 0x6F38 +#define SIM_EF_ACM 0x6F39 +#define SIM_EF_GID1 0x6F3E +#define SIM_EF_GID2 0x6F3F +#define SIM_EF_PUCT 0x6F41 +#define SIM_EF_CBMI 0x6F45 +#define SIM_EF_SPN 0x6F46 +#define SIM_EF_BCCH 0x6F74 +#define SIM_EF_ACC 0x6F78 +#define SIM_EF_FPLMN 0x6F7B +#define SIM_EF_LOCI 0x6F7E +#define SIM_EF_AD 0x6FAD +#define SIM_EF_PHASE 0x6FAE + +/* Select a file on the card */ +uint16_t sim_select(uint16_t fid) +{ + uint8_t txBuffer[2]; + uint8_t status_word[2]; + + txBuffer[1] = (uint8_t) fid; + txBuffer[0] = (uint8_t) (fid >> 8); + + if(calypso_sim_transceive(SIM_CLASS, SIM_SELECT, 0x00, 0x00, 0x02, + txBuffer, status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +/* Get the status of the currently selected file */ +uint16_t sim_status(void) +{ + uint8_t status_word[2]; + + if(calypso_sim_transceive(SIM_CLASS, SIM_STATUS, 0x00, 0x00, 0x00, 0, + status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +/* Read file in binary mode */ +uint16_t sim_readbinary(uint8_t offset_high, uint8_t offset_low, uint8_t length, uint8_t *data) +{ + uint8_t status_word[2]; + if(calypso_sim_transceive(SIM_CLASS, SIM_READ_BINARY, offset_high, + offset_low, length, data ,status_word, + SIM_APDU_GET) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +uint16_t sim_verify(char *pin) +{ + uint8_t txBuffer[8]; + uint8_t status_word[2]; + + memset(txBuffer, 0xFF, 8); + memcpy(txBuffer, pin, strlen(pin)); + + if(calypso_sim_transceive(SIM_CLASS, SIM_VERIFY_CHV, 0x00, 0x01, 0x08, txBuffer,status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +uint16_t sim_run_gsm_algorith(uint8_t *data) +{ + uint8_t status_word[2]; + + if(calypso_sim_transceive(SIM_CLASS, SIM_RUN_GSM_ALGORITHM, 0x00, 0x00, 0x10, data, status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + printf(" ==> Status word: %x\n", (status_word[0] << 8) | status_word[1]); + + if(status_word[0] != 0x9F || status_word[1] != 0x0C) + return (status_word[0] << 8) | status_word[1]; + + /* GET RESPONSE */ + + if(calypso_sim_transceive(SIM_CLASS, SIM_GET_RESPONSE, 0, 0, 0x0C, data ,status_word, SIM_APDU_GET) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + for (i= 0; i < ms*1300; i++) { i; } +} + +/* Execute my (dexter's) personal test */ +void do_sim_test(void) +{ + uint8_t testBuffer[20]; + uint8_t testtxBuffer[20]; + + uint8_t testDataBody[257]; + uint8_t testStatusWord[2]; + int recivedChars; + int i; + + uint8_t atr[20]; + uint8_t atrLength = 0; + + memset(atr,0,sizeof(atr)); + + + + uint8_t buffer[20]; + + + memset(testtxBuffer,0,sizeof(testtxBuffer)); + + puts("----------------SIMTEST----8<-----------------\n"); + + /* Initialize Sim-Controller driver */ + puts("Initializing driver:\n"); + calypso_sim_init(NULL); + + /* Power up sim and display ATR */ + puts("Power up simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_powerup(atr); + myHexdump(atr,atrLength); + + /* Reset sim and display ATR */ + puts("Reset simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_reset(atr); + myHexdump(atr,atrLength); + + + + testDataBody[0] = 0x3F; + testDataBody[1] = 0x00; + calypso_sim_transceive(0xA0, 0xA4, 0x00, 0x00, 0x02, testDataBody,0, SIM_APDU_PUT); + calypso_sim_transceive(0xA0, 0xC0, 0x00, 0x00, 0x0f, testDataBody,0, SIM_APDU_GET); + myHexdump(testDataBody,0x0F); + + puts("Test Phase 1: Testing bare sim commands...\n"); + + puts(" * Testing SELECT: Selecting MF\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_MF)); + + puts(" * Testing SELECT: Selecting DF_GSM\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_DF_GSM)); + + puts(" * Testing PIN VERIFY\n"); + printf(" ==> Status word: %x\n", sim_verify("1234")); + + puts(" * Testing SELECT: Selecting EF_IMSI\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_EF_IMSI)); + + puts(" * Testing STATUS:\n"); + printf(" ==> Status word: %x\n", sim_status()); + + memset(buffer,0,sizeof(buffer)); + puts(" * Testing READ BINARY:\n"); + printf(" ==> Status word: %x\n", sim_readbinary(0,0,9,buffer)); + printf(" Data: "); + myHexdump(buffer,9); + + memset(buffer,0,sizeof(buffer)); + memcpy(buffer,"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff",16); + puts(" * Testing RUN GSM ALGORITHM\n"); + printf(" ==> Status word: %x\n", sim_run_gsm_algorith(buffer)); + printf(" Result: "); + myHexdump(buffer,12); + + delay_ms(5000); + + calypso_sim_powerdown(); + + puts("------------END SIMTEST----8<-----------------\n"); +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +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); +} + +int main(void) +{ + board_init(); + + puts("\n\nOsmocomBB SIM Test (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); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + + do_sim_test(); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + } + + twl3025_power_off(); + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + default: + break; + } +} |