commit fbe7b94c9c65f2df74acd5dff7503c9833ec2579 Author: Harald Welte Date: Thu Feb 18 16:46:36 2010 +0100 Initial import of OsmocomBB into git repository diff --git a/doc/calypso-block.svg b/doc/calypso-block.svg new file mode 100644 index 000000000..6f8726762 --- /dev/null +++ b/doc/calypso-block.svg @@ -0,0 +1,707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + CALYPSO + + + + TWL3025 + BSP + USP + TSP + BUL + BDL + + + + AntennaSwitch + ASM4532 + + + + + + + + + TRF6151 + TransceiverMixersVCOPLL + + RF3166 + RF PA + TSP + + + GSM + + + + DCS/PCS + + + + GSM + + + + DCS + + + + PCS + + + RFCLK + + + + + I/Q Analog + + + + + CLK13M + + + + AFC Analog + + + TSP Parallel + TSP Serial + + + CLK32K + + + + GSM + + + + + DCS/PCS + + + + APC Analog + + + diff --git a/doc/calypso-gsm-notes.txt b/doc/calypso-gsm-notes.txt new file mode 100644 index 000000000..4377ceb40 --- /dev/null +++ b/doc/calypso-gsm-notes.txt @@ -0,0 +1,2 @@ +GSM burst duration: 577uS +RITA synthesizer retuning max: 170uS diff --git a/doc/calypso-signals.txt b/doc/calypso-signals.txt new file mode 100644 index 000000000..be49cf030 --- /dev/null +++ b/doc/calypso-signals.txt @@ -0,0 +1,184 @@ +CALYPSO PAD AND NAME I/O CONNECTS TO TO BFIN? +======================================================================================================== +C12 CLK32K_OUT O TWL3025:CLK32K (32kHz clock input) M +F12 CLK13M_OUT_START_BIT O TWL3025:CLK13M (master clock input) M +D12 nRESPWON I TWL3025:RESPWONz (dbb power-on reset @ batt insert) O +P1 EXT-FIQ I TWL3025:INT1 O +M3 EXT-IRQ I TWL3025:INT2 M +A12 TCXOEN O TRF6151:XSEL,XEN (enable 26MHz oscillator) M +F10 ON_OFF I TWL3025:ON_nOFF (dbb reset @ each switch on) O +B14 IT_WAKEUP O TWL3025:ITWAKEUP (real-time wake-up irq) O +B11 NEMU0 NC +E10 NEMU1 NC +D11 NBSCAN NC +D10 TDI I +C10 TDO O +B10 TCK I +E9 TMS I +L11 BFSR I TWL3025:BFSX (bb-serial tx frame sync) M +K10 BDR I TWL3025:BDX (bb-serial tx data) M +P12 BFSX O TWL3025:BFSR (bb-serial rx frame sync) M +M11 BDX O TWL3025:BDR (bb-serial rx data) M +N11 BCLKX-IO6 NC +P11 BCLKR-ARMCLK GND +P14 VDX O TWL3025:VDR (vb-serial rx data) +N13 VDR I TWL3025:VDX (vb-serial tx data) +M13 VFSRX I TWL3025:VFS (vb-serial frame sync) +N12 VCLKRX I TWL3025:VCK (vb-serial clock) +N7 MCUDI I TWL3025:UDX (spi output) M +M7 MCUDO O TWL3025:UDR (spi input) M +M8 MCUEN0 O TWL3025:UEN (spi enable) M +P8 MCUEN1-IO9 NC +L8 MCUEN2-IO13 NC +G13 SIM_IO IO TWL3025:DBBSIO (sim card shifter data) L +F13 SIM_CLK O TWL3025:DBBSCK (sim card shifter clock) L +G10 SIM_RST O TWL3025:DBBSRST (sim card shifter reset inp) L +G11 SIM_CD-MAS0 V_IO +F14 SIM_PWRCTRL-IO5 O TWL3025:DBBSIO (via 10k) L +B13 OSC32K_OUT O XTAL +C13 OSC32K_IN I XTAL +A14 VSSO GND +E13 CLKTCXO I TRF6151C:RFCLK (TCXO clock output) M +M2 IDDQ GND +N1 NI BOOT GND (via 100k) +N2 nRESET_OUT O NC +M4 IO3-SIM_RNW NC +L4 IO2-IRQ4 NC +P3 IO1-TPU_IDLE O S3C2410 +N3 IO0-TPU_WAIT NC +L7 LT-PWL NC +K7 BU-PWT NC +L6 KBR4-XDIO7 NC +N6 KBR3-XDIO6 NC +P6 KBR2-XDIO5 NC +M6 KBR1-XDIO4 NC +K6 KBR0-XDIO3 NC +M5 KBC4-XDIO2 NC +P5 KBC3-XDIO1 NC +L5 KBC2-XDIO0 NC +K5 KBC1-nIRQ NC +N4 KBC0-nFIQ NC +K9 MCSI_FSYNCH-IO12 NC +N10 MCSI_CLI-IO11 NC +M10 MCSI_RXD-IO10 NC +L10 MCSI_TXD-IO9 NC +C9 CTS_MODEM-X_F S3C24xx +D9 DSR_MODEM-LPG S3c24xx +E8 RTS_MODEM-TOUT S3c24xx +A9 RX_MODEM I S3c24xx +B9 TX_MODEM O S3c24xx +B8 SD_IRDA-CLKOUT_DSP NC +A8 RXIR_IRDA-X_A1 NC +C7 TXIR_IRDA-X_A4 NC +D8 RX_IRDA I socket +C8 TX_IRDA O socket +N9 NSCS(1)-X_A32 NC +L9 NSCS0-SCL NC +M9 SDI-SDA I V-IO +K8 SDO-nINT10 I NC +P9 SCLK-nINT1 I INT0 +I14 TSPCLKX O TRF6151:CLK (serial clock) M +H11 TSPDO O TWL3025:TDR,TRF6151:DATA (serial data) M +H10 DSPDI-IO4 I NC +H13 TSPEN0 O TWL3025:TEN (TSP enable) M +H12 TSPEN1 NC +H14 TSPEN2 TRF6151:STROBE (serial strobe) M +G12 TSPEN3-nSCS2 NC +M12 TSPACT0 O TRF6151:RESETz (serial interface reset) M +M14 TSPACT1 O ASM4532:VC2 M +L12 TSPACT2 O ASM4532:VC1 M +L13 TSPACT3 O RF3166:BAND_SELECT M +I10 TSPACT4-nRDYMEM O ASM4532:VC3 M +K11 TSPACT5-DPLLCLK NC +K13 TSPACT6-nCS6 NC +K12 TSPACT7-CLKX_SPI NC +K14 TSPACT8-nMREQ NC +I11 TSPACT9-MAS1 RF3166:TX_ENABLE (PA TX) M +I12 TSPACT10-nWAIT NC +I13 TSPACT11-MCLK NC +B7 DATA0 RAM/FLASH +D7 DATA1 RAM/FLASH +E7 DATA2 RAM/FLASH +D6 DATA3 RAM/FLASH +A6 DATA4 RAM/FLASH +C6 DATA5 RAM/FLASH +E6 DATA6 RAM/FLASH +C6 DATA7 RAM/FLASH +B5 DATA8 RAM/FLASH +D5 DATA9 RAM/FLASH +E5 DATA10 RAM/FLASH +B4 DATA11 RAM/FLASH +C4 DATA12 RAM/FLASH +D4 DATA13 RAM/FLASH +A3 DATA14 RAM/FLASH +B3 DATA15 RAM/FLASH +F3 ADD0 RAM/FLASH +F2 ADD1 RAM/FLASH +G5 ADD2 RAM/FLASH +G4 ADD3 RAM/FLASH +G2 ADD4 RAM/FLASH +G3 ADD5 RAM/FLASH +H1 ADD6 RAM/FLASH +H3 ADD7 RAM/FLASH +H2 ADD8 RAM/FLASH +H4 ADD9 RAM/FLASH +H5 ADD10 RAM/FLASH +I1 ADD11 RAM/FLASH +I2 ADD12 RAM/FLASH +I3 ADD13 RAM/FLASH +K3 ADD15 RAM/FLASH +K2 ADD16 RAM/FLASH +K4 ADD17 RAM/FLASH +I5 ADD18 RAM/FLASH +L1 ADD19 RAM/FLASH +L2 ADD20 RAM/FLASH +L3 ADD21-CLK16X_IRDA RAM/FLASH +B2 RNW RAM/FLASH +E3 nFEW-X_A0 NC +E2 nFOE-X_A3 RAM/FLASH +F4 FDP-nIACK RAM/FLASH +E4 nBLE-IO15 RAM/FLASH +F5 nBHE-IO14 RAM/FLASH +C2 nCS0 +C3 nCS1 RAM/FLASH +C1 nCS2 NC +D3 nCS3-nINT4 NC +D2 CS4-ADD22 RAM/FLASH +C11 nCS4-CO35 RAM/FLASH +A4 VDDS-MF1 V-FLASH +B6 VDDS-MF2 V-FLASH +G1 VDDS-MF3 V-FLASH +D1 VDDS-MF4 V-FLASH +A11 VDDS_2 V-IO +L14 VDDS_1 V-IO +N5 VDDS_1 V-IO +A5 VDD1 V-DBB +B12 VDD2 V-DBB +N14 VDD3 V-DBB +P7 VDD4 V-DBB +M1 VDD5 V-DBB +E1 VDD6 V-DBB +F1 VSS1 GND +N8 VSS2 GND +K1 VSS3 GND +P2 VSS4 GND +P4 VSS5 GND +P10 VSS6 GND +P13 VSS7 GND +G14 BSS8 GND +A10 VSS9 GND +A7 VSS10 GND +A2 VSS11 GND +B1 VSS12 GND +D13 VDDS_RTC V-RTC +D14 VDD_RTC V-RTC +C14 VSS_RTC GND +E11 VDD_ANG V-IO +E12 VSS_ANG GND +F11 VDD_PLL V-DBB +E14 VSS_PLL GND + +Other signals + +MODEM_ON +MODEM_RST diff --git a/doc/gsmdevboard-block.svg b/doc/gsmdevboard-block.svg new file mode 100644 index 000000000..cb19bc0ab --- /dev/null +++ b/doc/gsmdevboard-block.svg @@ -0,0 +1,746 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + TWL3025 + BSP + USP + TSP + BUL + BDL + + + + AntennaSwitch + ASM4532 + + + + + + + + + TRF6151 + TransceiverMixersVCOPLL + + RF3166 + RF PA + + + GSM + + + + DCS/PCS + + + + GSM + + + + DCS + + + + PCS + + + RFCLK + + + + + I/Q Analog + + + + + CLK13M + + + + AFC Analog + + + TSP Parallel + TSP Serial + + + CLK32K + + + + GSM + + + + + DCS/PCS + + + + APC Analog + + + + BF537 + TSP + + + Spartan3E + Ethernet + + + diff --git a/include/l1a_l23_interface.h b/include/l1a_l23_interface.h new file mode 100644 index 000000000..fc03d720c --- /dev/null +++ b/include/l1a_l23_interface.h @@ -0,0 +1,134 @@ +/* Messages to be sent between the different layers */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * + * 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. + * + */ + +#ifndef l1a_l23_interface_h +#define l1a_l23_interface_h + +#define SYNC_NEW_CCCH_REQ 1 +#define SYNC_NEW_CCCH_RESP 2 +#define CCCH_INFO_IND 3 +#define CCCH_RACH_REQ 4 +#define DEDIC_MODE_EST_REQ 5 +#define DEDIC_MODE_DATA_IND 6 +#define DEDIC_MODE_DATA_REQ 7 +#define LAYER1_RESET 8 + +/* + * NOTE: struct size. We do add manual padding out of the believe + * that it will avoid some unaligned access. + */ + +struct gsm_time { + uint32_t fn; /* FN count */ + uint16_t t1; /* FN div (26*51) */ + uint8_t t2; /* FN modulo 26 */ + uint8_t t3; /* FN modulo 51 */ + uint8_t tc; +}; + +/* + * downlink info ... down from the BTS.. + */ +struct l1_info_dl { + uint8_t msg_type; + uint8_t padding; + /* the ARFCN and the band. */ + uint16_t band_arfcn; + + struct gsm_time time; + uint8_t rx_level; + uint16_t snr[4]; +} __attribute__((packed)); + +/* new CCCH was found. This is following the header */ +struct l1_sync_new_ccch_resp { + uint8_t bsic; + uint8_t padding[3]; +} __attribute__((packed)); + +/* data on the CCCH was found. This is following the header */ +struct l1_ccch_info_ind { + uint8_t data[23]; +} __attribute__((packed)); + +/* + * uplink info + */ +struct l1_info_ul { + uint8_t msg_type; + uint8_t padding; + uint8_t tx_power; + uint8_t channel_number; + uint32_t tdma_frame; +} __attribute__((packed)); + +/* + * msg for SYNC_NEW_CCCH_REQ + * the l1_info_ul header is in front + */ +struct l1_sync_new_ccch_req { + uint16_t band_arfcn; +} __attribute__((packed)); + + +/* the l1_info_ul header is in front */ +struct l1_rach_req { + uint8_t ra; + uint8_t padding[3]; +} __attribute__((packed)); + +struct l1_dedic_mode_est_req { + struct l1_info_ul header; + uint16_t band_arfcn; + union { + struct { + uint8_t maio_high:4, + h:1, + tsc:3; + uint8_t hsn:6, + maio_low:2; + } h1; + struct { + uint8_t arfcn_high:2, + spare:2, + h:1, + tsc:3; + uint8_t arfcn_low; + } h0; + }; +} __attribute__((packed)); + +/* it is like the ccch ind... unite it? */ + +/* the l1_info_dl header is in front */ +struct l1_dedic_mode_data_ind { + uint8_t data[23]; +} __attribute__((packed)); + +/* the l1_info_ul header is in front */ +struct l1_dedic_mode_data_req { + uint8_t data[23]; +} __attribute__((packed)); + + +#endif diff --git a/src/host/calypso_pll/pll.pl b/src/host/calypso_pll/pll.pl new file mode 100755 index 000000000..52c91319f --- /dev/null +++ b/src/host/calypso_pll/pll.pl @@ -0,0 +1,10 @@ +#!/usr/bin/perl + +my $f_in = 26*1000*1000; + +for (my $mult = 1; $mult < 31; $mult++) { + for (my $div = 0; $div < 3; $div++) { + my $fout = $f_in * ($mult / ($div+1)); + printf("%03.1f MHz (mult=%2u, div=%1u)\n", $fout/(1000*1000), $mult, $div); + } +} diff --git a/src/host/layer2/.gitignore b/src/host/layer2/.gitignore new file mode 100644 index 000000000..d3594665b --- /dev/null +++ b/src/host/layer2/.gitignore @@ -0,0 +1,14 @@ +Makefile +Makefile.in +aclocal.m4 +configure +missing +*.o +depcomp +config.* +*.sw? +*.deps +layer2 +install-sh +autom4te.cache +*.a diff --git a/src/host/layer2/Makefile.am b/src/host/layer2/Makefile.am new file mode 100644 index 000000000..bc3910fa5 --- /dev/null +++ b/src/host/layer2/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +SUBDIRS = include src diff --git a/src/host/layer2/configure.ac b/src/host/layer2/configure.ac new file mode 100644 index 000000000..72ed2f82a --- /dev/null +++ b/src/host/layer2/configure.ac @@ -0,0 +1,24 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT + +AM_INIT_AUTOMAKE(layer2, 0.0.0) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + src/Makefile + include/Makefile + include/osmocom/Makefile + Makefile) diff --git a/src/host/layer2/include/Makefile.am b/src/host/layer2/include/Makefile.am new file mode 100644 index 000000000..ca774b6b9 --- /dev/null +++ b/src/host/layer2/include/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = l1a_l23_interface.h +SUBDIRS = osmocom diff --git a/src/host/layer2/include/l1a_l23_interface.h b/src/host/layer2/include/l1a_l23_interface.h new file mode 120000 index 000000000..2bbc9679a --- /dev/null +++ b/src/host/layer2/include/l1a_l23_interface.h @@ -0,0 +1 @@ +../../../../include/l1a_l23_interface.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/Makefile.am b/src/host/layer2/include/osmocom/Makefile.am new file mode 100644 index 000000000..7a5a088c8 --- /dev/null +++ b/src/host/layer2/include/osmocom/Makefile.am @@ -0,0 +1,4 @@ +# headers from OpenBSC +noinst_HEADERS = debug.h gsm_04_08.h linuxlist.h meas_rep.h msgb.h \ + select.h talloc.h timer.h +noinst_HEADERS += osmocom_layer2.h osmocom_data.h diff --git a/src/host/layer2/include/osmocom/debug.h b/src/host/layer2/include/osmocom/debug.h new file mode 120000 index 000000000..ae7c52c37 --- /dev/null +++ b/src/host/layer2/include/osmocom/debug.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/debug.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/gsm_04_08.h b/src/host/layer2/include/osmocom/gsm_04_08.h new file mode 120000 index 000000000..64dbbe0bd --- /dev/null +++ b/src/host/layer2/include/osmocom/gsm_04_08.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/gsm_04_08.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/linuxlist.h b/src/host/layer2/include/osmocom/linuxlist.h new file mode 120000 index 000000000..40851e842 --- /dev/null +++ b/src/host/layer2/include/osmocom/linuxlist.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/linuxlist.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/meas_rep.h b/src/host/layer2/include/osmocom/meas_rep.h new file mode 120000 index 000000000..2ab685619 --- /dev/null +++ b/src/host/layer2/include/osmocom/meas_rep.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/meas_rep.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/msgb.h b/src/host/layer2/include/osmocom/msgb.h new file mode 120000 index 000000000..eb0790086 --- /dev/null +++ b/src/host/layer2/include/osmocom/msgb.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/msgb.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/osmocom_data.h b/src/host/layer2/include/osmocom/osmocom_data.h new file mode 100644 index 000000000..dd930a2f0 --- /dev/null +++ b/src/host/layer2/include/osmocom/osmocom_data.h @@ -0,0 +1,22 @@ +#ifndef osmocom_data_h +#define osmocom_data_h + +#include + +/* taken from OpenBSC */ +enum gsm_band { + GSM_BAND_400, + GSM_BAND_850, + GSM_BAND_900, + GSM_BAND_1800, + GSM_BAND_1900, +}; + +/* One Mobilestation for osmocom */ +struct osmocom_ms { + struct bsc_fd bfd; + enum gsm_band band; + int arfcn; +}; + +#endif diff --git a/src/host/layer2/include/osmocom/osmocom_layer2.h b/src/host/layer2/include/osmocom/osmocom_layer2.h new file mode 100644 index 000000000..af71873c7 --- /dev/null +++ b/src/host/layer2/include/osmocom/osmocom_layer2.h @@ -0,0 +1,12 @@ +#ifndef osmocom_layer2_h +#define osmocom_layer2_h + +#include + +struct osmocom_ms; + +int osmo_recv(struct osmocom_ms *ms, struct msgb *msg); + +extern int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg); + +#endif diff --git a/src/host/layer2/include/osmocom/select.h b/src/host/layer2/include/osmocom/select.h new file mode 120000 index 000000000..557060f73 --- /dev/null +++ b/src/host/layer2/include/osmocom/select.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/select.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/talloc.h b/src/host/layer2/include/osmocom/talloc.h new file mode 120000 index 000000000..7929944bc --- /dev/null +++ b/src/host/layer2/include/osmocom/talloc.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/talloc.h \ No newline at end of file diff --git a/src/host/layer2/include/osmocom/timer.h b/src/host/layer2/include/osmocom/timer.h new file mode 120000 index 000000000..7cffef202 --- /dev/null +++ b/src/host/layer2/include/osmocom/timer.h @@ -0,0 +1 @@ +../../../libosmocom/include/osmocom/timer.h \ No newline at end of file diff --git a/src/host/layer2/src/Makefile.am b/src/host/layer2/src/Makefile.am new file mode 100644 index 000000000..5cca4afa5 --- /dev/null +++ b/src/host/layer2/src/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall + +noinst_LIBRARIES = libosmocom.a +libosmocom_a_SOURCES = libosmocom/debug.c libosmocom/msgb.c libosmocom/select.c \ + libosmocom/talloc.c libosmocom/timer.c + +sbin_PROGRAMS = layer2 +layer2_SOURCES = layer2_main.c osmocom_layer2.c +layer2_LDADD = libosmocom.a diff --git a/src/host/layer2/src/layer2_main.c b/src/host/layer2/src/layer2_main.c new file mode 100644 index 000000000..c84cce495 --- /dev/null +++ b/src/host/layer2/src/layer2_main.c @@ -0,0 +1,205 @@ +/* Main method of the layer2 stack */ +/* (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include + +#include +#include +#include + +#include + +#include +#include + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#define GSM_L2_LENGTH 256 + +static void *l2_ctx = NULL; +static char *socket_path = "/tmp/osmocom_l2"; +static struct osmocom_ms *ms = NULL; + +static int layer2_read(struct bsc_fd *fd, unsigned int flags) +{ + struct msgb *msg; + u_int16_t len; + int rc; + + msg = msgb_alloc(GSM_L2_LENGTH, "Layer2"); + if (!msg) { + fprintf(stderr, "Failed to allocate msg.\n"); + return -1; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "Short read. Error.\n"); + exit(2); + } + + if (ntohs(len) > GSM_L2_LENGTH) { + fprintf(stderr, "Length is too big: %u\n", ntohs(len)); + msgb_free(msg); + return -1; + } + + + /* blocking read for the poor... we can starve in here... */ + msg->l2h = msgb_put(msg, ntohs(len)); + rc = read(fd->fd, msg->l2h, msgb_l2len(msg)); + if (rc != msgb_l2len(msg)) { + fprintf(stderr, "Can not read data: rc: %d errno: %d\n", rc, errno); + msgb_free(msg); + return -1; + } + + osmo_recv((struct osmocom_ms *) fd->data, msg); + msgb_free(msg); + return 0; +} + +int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + uint16_t *len; + + LOGP(DMUX, LOGL_INFO, "Sending: '%s'\n", hexdump(msg->data, msg->len)); + + + len = (uint16_t *) msgb_push(msg, sizeof(*len)); + *len = htons(msg->len - sizeof(*len)); + + /* TODO: just enqueue the message and wait for ready write.. */ + rc = write(ms->bfd.fd, msg->data, msg->len); + if (rc != msg->len) { + fprintf(stderr, "Failed to write data: rc: %d\n", rc); + msgb_free(msg); + return -1; + } + + msgb_free(msg); + return 0; +} + +static void print_usage() +{ + printf("Usage: ./layer2\n"); +} + +static void print_help() +{ + printf(" Some help...\n"); + printf(" -h --help this text\n"); + printf(" -s --socket /tmp/osmocom_l2. Path to the unix domain socket\n"); + printf(" -a --arfcn NR. The ARFCN to be used for layer2.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"socket", 1, 0, 's'}, + {"arfcn", 1, 0, 'a'}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hs:a:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + break; + case 's': + socket_path = talloc_strdup(l2_ctx, optarg); + break; + case 'a': + ms->arfcn = atoi(optarg); + break; + default: + break; + } + } +} + +int main(int argc, char **argv) +{ + int rc; + struct sockaddr_un local; + + l2_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + ms = talloc_zero(l2_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + exit(1); + } + + ms->arfcn = 871; + + handle_options(argc, argv); + + ms->bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ms->bfd.fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + exit(1); + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + rc = connect(ms->bfd.fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path); + exit(1); + } + + ms->bfd.when = BSC_FD_READ; + ms->bfd.cb = layer2_read; + ms->bfd.data = ms; + + if (bsc_register_fd(&ms->bfd) != 0) { + fprintf(stderr, "Failed to register fd.\n"); + exit(1); + } + + while (1) { + bsc_select_main(0); + } + + + return 0; +} diff --git a/src/host/layer2/src/libosmocom/debug.c b/src/host/layer2/src/libosmocom/debug.c new file mode 120000 index 000000000..acf426112 --- /dev/null +++ b/src/host/layer2/src/libosmocom/debug.c @@ -0,0 +1 @@ +../../../libosmocom/src/debug.c \ No newline at end of file diff --git a/src/host/layer2/src/libosmocom/msgb.c b/src/host/layer2/src/libosmocom/msgb.c new file mode 120000 index 000000000..42624f80c --- /dev/null +++ b/src/host/layer2/src/libosmocom/msgb.c @@ -0,0 +1 @@ +../../../libosmocom/src/msgb.c \ No newline at end of file diff --git a/src/host/layer2/src/libosmocom/select.c b/src/host/layer2/src/libosmocom/select.c new file mode 120000 index 000000000..005d21bbb --- /dev/null +++ b/src/host/layer2/src/libosmocom/select.c @@ -0,0 +1 @@ +../../../libosmocom/src/select.c \ No newline at end of file diff --git a/src/host/layer2/src/libosmocom/signal.c b/src/host/layer2/src/libosmocom/signal.c new file mode 120000 index 000000000..d1651c395 --- /dev/null +++ b/src/host/layer2/src/libosmocom/signal.c @@ -0,0 +1 @@ +../../../libosmocom/src/signal.c \ No newline at end of file diff --git a/src/host/layer2/src/libosmocom/talloc.c b/src/host/layer2/src/libosmocom/talloc.c new file mode 120000 index 000000000..dcc53fa77 --- /dev/null +++ b/src/host/layer2/src/libosmocom/talloc.c @@ -0,0 +1 @@ +../../../libosmocom/src/talloc.c \ No newline at end of file diff --git a/src/host/layer2/src/libosmocom/timer.c b/src/host/layer2/src/libosmocom/timer.c new file mode 120000 index 000000000..142a2aa0d --- /dev/null +++ b/src/host/layer2/src/libosmocom/timer.c @@ -0,0 +1 @@ +../../../libosmocom/src/timer.c \ No newline at end of file diff --git a/src/host/layer2/src/osmocom_layer2.c b/src/host/layer2/src/osmocom_layer2.c new file mode 100644 index 000000000..0be0a4c42 --- /dev/null +++ b/src/host/layer2/src/osmocom_layer2.c @@ -0,0 +1,226 @@ +/* Layer2 handling code... LAPD and stuff + * + * (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include +#include +#include + +#include +#include +#include + + +static struct msgb *osmo_l1_alloc(uint8_t msg_type) +{ + struct l1_info_ul *ul; + struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1"); + + if (!msg) { + fprintf(stderr, "Failed to allocate memory.\n"); + return NULL; + } + + ul = (struct l1_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->msg_type = msg_type; + + return msg; +} + +static int osmo_make_band_arfcn(struct osmocom_ms *ms) +{ + /* TODO: Include the band */ + return ms->arfcn; +} + +static int osmo_l2_ccch_resp(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1_info_dl *dl; + struct l1_sync_new_ccch_resp *sb; + + if (msgb_l3len(msg) < sizeof(*sb)) { + fprintf(stderr, "MSG too short for CCCH RESP: %u\n", msgb_l3len(msg)); + return -1; + } + + dl = (struct l1_info_dl *) msg->l2h; + sb = (struct l1_sync_new_ccch_resp *) msg->l3h; + + printf("Found sync burst: SNR: %u TDMA: (%.4u/%.2u/%.2u) bsic: %d\n", + dl->snr[0], dl->time.t1, dl->time.t2, dl->time.t3, sb->bsic); + return 0; +} + +static void dump_bcch(u_int8_t tc, const uint8_t *data) +{ + struct gsm48_system_information_type_header *si_hdr; + si_hdr = (struct gsm48_system_information_type_header *) data;; + + /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */ + switch (si_hdr->system_information) { + case GSM48_MT_RR_SYSINFO_1: + fprintf(stderr, "\tSI1"); + if (tc != 0) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_2: + fprintf(stderr, "\tSI2"); + if (tc != 1) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_3: + fprintf(stderr, "\tSI3"); + if (tc != 2 && tc != 6) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_4: + fprintf(stderr, "\tSI4"); + if (tc != 3 && tc != 7) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_5: + fprintf(stderr, "\tSI5"); + break; + case GSM48_MT_RR_SYSINFO_6: + fprintf(stderr, "\tSI6"); + break; + case GSM48_MT_RR_SYSINFO_7: + fprintf(stderr, "\tSI7"); + if (tc != 7) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_8: + fprintf(stderr, "\tSI8"); + if (tc != 3) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_9: + fprintf(stderr, "\tSI9"); + if (tc != 4) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_13: + fprintf(stderr, "\tSI13"); + if (tc != 4 && tc != 0) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_16: + fprintf(stderr, "\tSI16"); + if (tc != 6) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_17: + fprintf(stderr, "\tSI17"); + if (tc != 2) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_2bis: + fprintf(stderr, "\tSI2bis"); + if (tc != 5) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_2ter: + fprintf(stderr, "\tSI2ter"); + if (tc != 5 && tc != 4) + fprintf(stderr, " on wrong TC"); + break; + case GSM48_MT_RR_SYSINFO_5bis: + fprintf(stderr, "\tSI5bis"); + break; + case GSM48_MT_RR_SYSINFO_5ter: + fprintf(stderr, "\tSI5ter"); + break; + default: + fprintf(stderr, "\tUnknown SI"); + break; + }; + + fprintf(stderr, "\n"); +} + +static int osmo_l2_ccch_data(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1_info_dl *dl; + struct l1_ccch_info_ind *ccch; + + if (msgb_l3len(msg) < sizeof(*ccch)) { + fprintf(stderr, "MSG too short CCCH Data Ind: %u\n", msgb_l3len(msg)); + return -1; + } + + dl = (struct l1_info_dl *) msg->l2h; + ccch = (struct l1_ccch_info_ind *) msg->l3h; + printf("Found CCCH burst(s): TDMA: (%.4u/%.2u/%.2u) tc:%d %s si: 0x%x\n", + dl->time.t1, dl->time.t2, dl->time.t3, dl->time.tc, + hexdump(ccch->data, sizeof(ccch->data)), ccch->data[2]); + + dump_bcch(dl->time.tc, ccch->data); + return 0; +} + +static int osmo_l1_reset(struct osmocom_ms *ms) +{ + struct msgb *msg; + struct l1_sync_new_ccch_req *req; + + msg = osmo_l1_alloc(SYNC_NEW_CCCH_REQ); + if (!msg) + return -1; + + printf("Layer1 Reset.\n"); + req = (struct l1_sync_new_ccch_req *) msgb_put(msg, sizeof(*req)); + req->band_arfcn = osmo_make_band_arfcn(ms); + + return osmo_send_l1(ms, msg); +} + + +int osmo_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc = 0; + struct l1_info_dl *dl; + + if (msgb_l2len(msg) < sizeof(*dl)) { + fprintf(stderr, "Short Layer2 message: %u\n", msgb_l2len(msg)); + return -1; + } + + dl = (struct l1_info_dl *) msg->l2h; + msg->l3h = &msg->l2h[0] + sizeof(*dl); + + switch (dl->msg_type) { + case SYNC_NEW_CCCH_RESP: + rc = osmo_l2_ccch_resp(ms, msg); + break; + case CCCH_INFO_IND: + rc = osmo_l2_ccch_data(ms, msg); + break; + case LAYER1_RESET: + rc = osmo_l1_reset(ms); + break; + default: + fprintf(stderr, "Unknown MSG: %u\n", dl->msg_type); + break; + } + + return rc; +} diff --git a/src/host/libosmocom/README b/src/host/libosmocom/README new file mode 100644 index 000000000..1a97beac5 --- /dev/null +++ b/src/host/libosmocom/README @@ -0,0 +1 @@ +This is a copy of OpenBSC header and sources. Do not edit them. diff --git a/src/host/libosmocom/include/osmocom/debug.h b/src/host/libosmocom/include/osmocom/debug.h new file mode 100644 index 000000000..c40eec3fb --- /dev/null +++ b/src/host/libosmocom/include/osmocom/debug.h @@ -0,0 +1,131 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#include +#include "linuxlist.h" + +#define DEBUG + +/* Debug Areas of the code */ +enum { + DRLL, + DCC, + DMM, + DRR, + DRSL, + DNM, + DMNCC, + DSMS, + DPAG, + DMEAS, + DMI, + DMIB, + DMUX, + DINP, + DSCCP, + DMSC, + DMGCP, + DHO, + DDB, + DREF, + Debug_LastEntry, +}; + +#ifdef DEBUG +#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args) +#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args) +#else +#define DEBUGP(xss, fmt, args...) +#define DEBUGPC(ss, fmt, args...) +#endif + + +#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; + +char *hexdump(const unsigned char *buf, int len); +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); + +/* new logging interface */ +#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) +#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) + +/* different levels */ +#define LOGL_DEBUG 1 /* debugging information */ +#define LOGL_INFO 3 +#define LOGL_NOTICE 5 /* abnormal/unexpected condition */ +#define LOGL_ERROR 7 /* error condition, requires user action */ +#define LOGL_FATAL 8 /* fatal, program aborted */ + +/* context */ +#define BSC_CTX_LCHAN 0 +#define BSC_CTX_SUBSCR 1 +#define BSC_CTX_BTS 2 +#define BSC_CTX_SCCP 3 + +/* target */ + +enum { + DEBUG_FILTER_IMSI = 1 << 0, + DEBUG_FILTER_ALL = 1 << 1, +}; + +struct debug_category { + int enabled; + int loglevel; +}; + +struct debug_target { + int filter_map; + char *imsi_filter; + + + struct debug_category categories[Debug_LastEntry]; + int use_color; + int print_timestamp; + int loglevel; + + union { + struct { + FILE *out; + } tgt_stdout; + + struct { + int priority; + } tgt_syslog; + + struct { + void *vty; + } tgt_vty; + }; + + void (*output) (struct debug_target *target, const char *string); + + struct llist_head entry; +}; + +/* use the above macros */ +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7))); +void debug_init(void); + +/* context management */ +void debug_reset_context(void); +void debug_set_context(int ctx, void *value); + +/* filter on the targets */ +void debug_set_imsi_filter(struct debug_target *target, const char *imsi); +void debug_set_all_filter(struct debug_target *target, int); +void debug_set_use_color(struct debug_target *target, int); +void debug_set_print_timestamp(struct debug_target *target, int); +void debug_set_log_level(struct debug_target *target, int log_level); +void debug_parse_category_mask(struct debug_target *target, const char* mask); +int debug_parse_level(const char *lvl); +int debug_parse_category(const char *category); +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level); + + +/* management of the targets */ +struct debug_target *debug_target_create(void); +struct debug_target *debug_target_create_stderr(void); +void debug_add_target(struct debug_target *target); +void debug_del_target(struct debug_target *target); +#endif /* _DEBUG_H */ diff --git a/src/host/libosmocom/include/osmocom/gsm_04_08.h b/src/host/libosmocom/include/osmocom/gsm_04_08.h new file mode 100644 index 000000000..84b9b5d1e --- /dev/null +++ b/src/host/libosmocom/include/osmocom/gsm_04_08.h @@ -0,0 +1,796 @@ +#ifndef _GSM_04_08_H +#define _GSM_04_08_H + +#include + +/* GSM TS 04.08 definitions */ +struct gsm_lchan; + +struct gsm48_classmark1 { + u_int8_t spare:1, + rev_level:2, + es_ind:1, + a5_1:1, + pwr_lev:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.5 */ +struct gsm48_chan_desc { + u_int8_t chan_nr; + union { + struct { + u_int8_t maio_high:4, + h:1, + tsc:3; + u_int8_t hsn:6, + maio_low:2; + } h1; + struct { + u_int8_t arfcn_high:2, + spare:2, + h:1, + tsc:3; + u_int8_t arfcn_low; + } h0; + }; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.21aa */ +struct gsm48_multi_rate_conf { + u_int8_t smod : 2, + spare: 1, + icmi : 1, + nscb : 1, + ver : 3; + u_int8_t m4_75 : 1, + m5_15 : 1, + m5_90 : 1, + m6_70 : 1, + m7_40 : 1, + m7_95 : 1, + m10_2 : 1, + m12_2 : 1; +} __attribute__((packed)); + +/* Chapter 10.5.2.30 */ +struct gsm48_req_ref { + u_int8_t ra; + u_int8_t t3_high:3, + t1_:5; + u_int8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* + * Chapter 9.1.5/9.1.6 + * + * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a + */ +struct gsm48_chan_mode_modify { + struct gsm48_chan_desc chan_desc; + u_int8_t mode; +} __attribute__ ((packed)); + +enum gsm48_chan_mode { + GSM48_CMODE_SIGN = 0x00, + GSM48_CMODE_SPEECH_V1 = 0x01, + GSM48_CMODE_SPEECH_EFR = 0x21, + GSM48_CMODE_SPEECH_AMR = 0x41, + GSM48_CMODE_DATA_14k5 = 0x0f, + GSM48_CMODE_DATA_12k0 = 0x03, + GSM48_CMODE_DATA_6k0 = 0x0b, + GSM48_CMODE_DATA_3k6 = 0x23, +}; + +/* Chapter 9.1.2 */ +struct gsm48_ass_cmd { + /* Semantic is from 10.5.2.5a */ + struct gsm48_chan_desc chan_desc; + u_int8_t power_command; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.2.2 */ +struct gsm48_cell_desc { + u_int8_t bcc:3, + ncc:3, + arfcn_hi:2; + u_int8_t arfcn_lo; +} __attribute__((packed)); + +/* Chapter 9.1.15 */ +struct gsm48_ho_cmd { + struct gsm48_cell_desc cell_desc; + struct gsm48_chan_desc chan_desc; + u_int8_t ho_ref; + u_int8_t power_command; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.1.18 */ +struct gsm48_imm_ass { + u_int8_t l2_plen; + u_int8_t proto_discr; + u_int8_t msg_type; + u_int8_t page_mode; + struct gsm48_chan_desc chan_desc; + struct gsm48_req_ref req_ref; + u_int8_t timing_advance; + u_int8_t mob_alloc_len; + u_int8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.3 */ +struct gsm48_loc_area_id { + u_int8_t digits[3]; /* BCD! */ + u_int16_t lac; +} __attribute__ ((packed)); + +/* Section 9.2.2 */ +struct gsm48_auth_req { + u_int8_t key_seq:4, + spare:4; + u_int8_t rand[16]; +} __attribute__ ((packed)); + +/* Section 9.2.15 */ +struct gsm48_loc_upd_req { + u_int8_t type:4, + key_seq:4; + struct gsm48_loc_area_id lai; + struct gsm48_classmark1 classmark1; + u_int8_t mi_len; + u_int8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.1 */ +struct gsm48_hdr { + u_int8_t proto_discr; + u_int8_t msg_type; + u_int8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.3x System information Type header */ +struct gsm48_system_information_type_header { + u_int8_t l2_plen; + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; +} __attribute__ ((packed)); + +struct gsm48_rach_control { + u_int8_t re :1, + cell_bar :1, + tx_integer :4, + max_trans :2; + u_int8_t t2; + u_int8_t t3; +} __attribute__ ((packed)); + +/* Section 10.5.2.4 Cell Selection Parameters */ +struct gsm48_cell_sel_par { + u_int8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */ + cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */ + u_int8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */ + neci:1, + acs:1; +} __attribute__ ((packed)); + +/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */ +struct gsm48_control_channel_descr { + u_int8_t ccch_conf :3, + bs_ag_blks_res :3, + att :1, + spare1 :1; + u_int8_t bs_pa_mfrms : 3, + spare2 :5; + u_int8_t t3212; +} __attribute__ ((packed)); + +struct gsm48_cell_options { + u_int8_t radio_link_timeout:4, + dtx:2, + pwrc:1, + spare:1; +} __attribute__ ((packed)); + +/* Section 9.2.9 CM service request */ +struct gsm48_service_request { + u_int8_t cm_service_type : 4, + cipher_key_seq : 4; + /* length + 3 bytes */ + u_int32_t classmark; + u_int8_t mi_len; + u_int8_t mi[0]; + /* optional priority level */ +} __attribute__ ((packed)); + +/* Section 9.1.31 System information Type 1 */ +struct gsm48_system_information_type_1 { + struct gsm48_system_information_type_header header; + u_int8_t cell_channel_description[16]; + struct gsm48_rach_control rach_control; + u_int8_t rest_octets[0]; /* NCH position on the CCCH */ +} __attribute__ ((packed)); + +/* Section 9.1.32 System information Type 2 */ +struct gsm48_system_information_type_2 { + struct gsm48_system_information_type_header header; + u_int8_t bcch_frequency_list[16]; + u_int8_t ncc_permitted; + struct gsm48_rach_control rach_control; +} __attribute__ ((packed)); + +/* Section 9.1.35 System information Type 3 */ +struct gsm48_system_information_type_3 { + struct gsm48_system_information_type_header header; + u_int16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_control_channel_descr control_channel_desc; + struct gsm48_cell_options cell_options; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.36 System information Type 4 */ +struct gsm48_system_information_type_4 { + struct gsm48_system_information_type_header header; + struct gsm48_loc_area_id lai; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + /* optional CBCH conditional CBCH... followed by + mandantory SI 4 Reset Octets + */ + u_int8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.37 System information Type 5 */ +struct gsm48_system_information_type_5 { + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; + u_int8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.40 System information Type 6 */ +struct gsm48_system_information_type_6 { + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; + u_int16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_cell_options cell_options; + u_int8_t ncc_permitted; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.43a System Information type 13 */ +struct gsm48_system_information_type_13 { + struct gsm48_system_information_type_header header; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.2.12 IMSI Detach Indication */ +struct gsm48_imsi_detach_ind { + struct gsm48_classmark1 classmark1; + u_int8_t mi_len; + u_int8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.2 + GSM 04.07 12.2.3.1.1 */ +#define GSM48_PDISC_GROUP_CC 0x00 +#define GSM48_PDISC_BCAST_CC 0x01 +#define GSM48_PDISC_PDSS1 0x02 +#define GSM48_PDISC_CC 0x03 +#define GSM48_PDISC_PDSS2 0x04 +#define GSM48_PDISC_MM 0x05 +#define GSM48_PDISC_RR 0x06 +#define GSM48_PDISC_MM_GPRS 0x08 +#define GSM48_PDISC_SMS 0x09 +#define GSM48_PDISC_SM_GPRS 0x0a +#define GSM48_PDISC_NC_SS 0x0b +#define GSM48_PDISC_LOC 0x0c +#define GSM48_PDISC_MASK 0x0f +#define GSM48_PDISC_USSD 0x11 + +/* Section 10.4 */ +#define GSM48_MT_RR_INIT_REQ 0x3c +#define GSM48_MT_RR_ADD_ASS 0x3b +#define GSM48_MT_RR_IMM_ASS 0x3f +#define GSM48_MT_RR_IMM_ASS_EXT 0x39 +#define GSM48_MT_RR_IMM_ASS_REJ 0x3a + +#define GSM48_MT_RR_CIPH_M_CMD 0x35 +#define GSM48_MT_RR_CIPH_M_COMPL 0x32 + +#define GSM48_MT_RR_CFG_CHG_CMD 0x30 +#define GSM48_MT_RR_CFG_CHG_ACK 0x31 +#define GSM48_MT_RR_CFG_CHG_REJ 0x33 + +#define GSM48_MT_RR_ASS_CMD 0x2e +#define GSM48_MT_RR_ASS_COMPL 0x29 +#define GSM48_MT_RR_ASS_FAIL 0x2f +#define GSM48_MT_RR_HANDO_CMD 0x2b +#define GSM48_MT_RR_HANDO_COMPL 0x2c +#define GSM48_MT_RR_HANDO_FAIL 0x28 +#define GSM48_MT_RR_HANDO_INFO 0x2d + +#define GSM48_MT_RR_CELL_CHG_ORDER 0x08 +#define GSM48_MT_RR_PDCH_ASS_CMD 0x23 + +#define GSM48_MT_RR_CHAN_REL 0x0d +#define GSM48_MT_RR_PART_REL 0x0a +#define GSM48_MT_RR_PART_REL_COMP 0x0f + +#define GSM48_MT_RR_PAG_REQ_1 0x21 +#define GSM48_MT_RR_PAG_REQ_2 0x22 +#define GSM48_MT_RR_PAG_REQ_3 0x24 +#define GSM48_MT_RR_PAG_RESP 0x27 +#define GSM48_MT_RR_NOTIF_NCH 0x20 +#define GSM48_MT_RR_NOTIF_FACCH 0x25 +#define GSM48_MT_RR_NOTIF_RESP 0x26 + +#define GSM48_MT_RR_SYSINFO_8 0x18 +#define GSM48_MT_RR_SYSINFO_1 0x19 +#define GSM48_MT_RR_SYSINFO_2 0x1a +#define GSM48_MT_RR_SYSINFO_3 0x1b +#define GSM48_MT_RR_SYSINFO_4 0x1c +#define GSM48_MT_RR_SYSINFO_5 0x1d +#define GSM48_MT_RR_SYSINFO_6 0x1e +#define GSM48_MT_RR_SYSINFO_7 0x1f + +#define GSM48_MT_RR_SYSINFO_2bis 0x02 +#define GSM48_MT_RR_SYSINFO_2ter 0x03 +#define GSM48_MT_RR_SYSINFO_5bis 0x05 +#define GSM48_MT_RR_SYSINFO_5ter 0x06 +#define GSM48_MT_RR_SYSINFO_9 0x04 +#define GSM48_MT_RR_SYSINFO_13 0x00 + +#define GSM48_MT_RR_SYSINFO_16 0x3d +#define GSM48_MT_RR_SYSINFO_17 0x3e + +#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10 +#define GSM48_MT_RR_STATUS 0x12 +#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17 +#define GSM48_MT_RR_FREQ_REDEF 0x14 +#define GSM48_MT_RR_MEAS_REP 0x15 +#define GSM48_MT_RR_CLSM_CHG 0x16 +#define GSM48_MT_RR_CLSM_ENQ 0x13 +#define GSM48_MT_RR_EXT_MEAS_REP 0x36 +#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37 +#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34 + +#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08 +#define GSM48_MT_RR_UPLINK_RELEASE 0x0e +#define GSM48_MT_RR_UPLINK_FREE 0x0c +#define GSM48_MT_RR_UPLINK_BUSY 0x2a +#define GSM48_MT_RR_TALKER_IND 0x11 + +#define GSM48_MT_RR_APP_INFO 0x38 + +/* Table 10.2/3GPP TS 04.08 */ +#define GSM48_MT_MM_IMSI_DETACH_IND 0x01 +#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02 +#define GSM48_MT_MM_LOC_UPD_REJECT 0x04 +#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08 + +#define GSM48_MT_MM_AUTH_REJ 0x11 +#define GSM48_MT_MM_AUTH_REQ 0x12 +#define GSM48_MT_MM_AUTH_RESP 0x14 +#define GSM48_MT_MM_ID_REQ 0x18 +#define GSM48_MT_MM_ID_RESP 0x19 +#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a +#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b + +#define GSM48_MT_MM_CM_SERV_ACC 0x21 +#define GSM48_MT_MM_CM_SERV_REJ 0x22 +#define GSM48_MT_MM_CM_SERV_ABORT 0x23 +#define GSM48_MT_MM_CM_SERV_REQ 0x24 +#define GSM48_MT_MM_CM_SERV_PROMPT 0x25 +#define GSM48_MT_MM_CM_REEST_REQ 0x28 +#define GSM48_MT_MM_ABORT 0x29 + +#define GSM48_MT_MM_NULL 0x30 +#define GSM48_MT_MM_STATUS 0x31 +#define GSM48_MT_MM_INFO 0x32 + +/* Table 10.3/3GPP TS 04.08 */ +#define GSM48_MT_CC_ALERTING 0x01 +#define GSM48_MT_CC_CALL_CONF 0x08 +#define GSM48_MT_CC_CALL_PROC 0x02 +#define GSM48_MT_CC_CONNECT 0x07 +#define GSM48_MT_CC_CONNECT_ACK 0x0f +#define GSM48_MT_CC_EMERG_SETUP 0x0e +#define GSM48_MT_CC_PROGRESS 0x03 +#define GSM48_MT_CC_ESTAB 0x04 +#define GSM48_MT_CC_ESTAB_CONF 0x06 +#define GSM48_MT_CC_RECALL 0x0b +#define GSM48_MT_CC_START_CC 0x09 +#define GSM48_MT_CC_SETUP 0x05 + +#define GSM48_MT_CC_MODIFY 0x17 +#define GSM48_MT_CC_MODIFY_COMPL 0x1f +#define GSM48_MT_CC_MODIFY_REJECT 0x13 +#define GSM48_MT_CC_USER_INFO 0x10 +#define GSM48_MT_CC_HOLD 0x18 +#define GSM48_MT_CC_HOLD_ACK 0x19 +#define GSM48_MT_CC_HOLD_REJ 0x1a +#define GSM48_MT_CC_RETR 0x1c +#define GSM48_MT_CC_RETR_ACK 0x1d +#define GSM48_MT_CC_RETR_REJ 0x1e + +#define GSM48_MT_CC_DISCONNECT 0x25 +#define GSM48_MT_CC_RELEASE 0x2d +#define GSM48_MT_CC_RELEASE_COMPL 0x2a + +#define GSM48_MT_CC_CONG_CTRL 0x39 +#define GSM48_MT_CC_NOTIFY 0x3e +#define GSM48_MT_CC_STATUS 0x3d +#define GSM48_MT_CC_STATUS_ENQ 0x34 +#define GSM48_MT_CC_START_DTMF 0x35 +#define GSM48_MT_CC_STOP_DTMF 0x31 +#define GSM48_MT_CC_STOP_DTMF_ACK 0x32 +#define GSM48_MT_CC_START_DTMF_ACK 0x36 +#define GSM48_MT_CC_START_DTMF_REJ 0x37 +#define GSM48_MT_CC_FACILITY 0x3a + +/* FIXME: Table 10.4 / 10.4a (GPRS) */ + +/* Section 10.5.2.26, Table 10.5.64 */ +#define GSM48_PM_MASK 0x03 +#define GSM48_PM_NORMAL 0x00 +#define GSM48_PM_EXTENDED 0x01 +#define GSM48_PM_REORG 0x02 +#define GSM48_PM_SAME 0x03 + +/* Chapter 10.5.3.5 / Table 10.5.93 */ +#define GSM48_LUPD_NORMAL 0x0 +#define GSM48_LUPD_PERIODIC 0x1 +#define GSM48_LUPD_IMSI_ATT 0x2 +#define GSM48_LUPD_RESERVED 0x3 + +/* Table 10.5.4 */ +#define GSM_MI_TYPE_MASK 0x07 +#define GSM_MI_TYPE_NONE 0x00 +#define GSM_MI_TYPE_IMSI 0x01 +#define GSM_MI_TYPE_IMEI 0x02 +#define GSM_MI_TYPE_IMEISV 0x03 +#define GSM_MI_TYPE_TMSI 0x04 +#define GSM_MI_ODD 0x08 + +#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */ +#define GSM48_IE_MOBILE_ID 0x17 +#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */ +#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */ +#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */ +#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */ +#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */ + +#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */ +#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */ +#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */ +#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */ +#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */ +#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */ +#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */ +#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */ +#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */ +#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */ +#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */ +#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */ +#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */ +#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */ +#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */ +#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */ +#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */ +#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */ +#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */ +#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */ +#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */ +#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */ +#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */ +#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */ +#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */ +#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */ +#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */ +#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */ + +/* Section 10.5.4.11 / Table 10.5.122 */ +#define GSM48_CAUSE_CS_GSM 0x60 + +/* Section 9.1.2 / Table 9.3 */ +#define GSM48_IE_FRQLIST_AFTER 0x05 +#define GSM48_IE_CELL_CH_DESC 0x62 +#define GSM48_IE_MSLOT_DESC 0x10 +#define GSM48_IE_CHANMODE_1 0x63 +#define GSM48_IE_CHANMODE_2 0x11 +#define GSM48_IE_CHANMODE_3 0x13 +#define GSM48_IE_CHANMODE_4 0x14 +#define GSM48_IE_CHANMODE_5 0x15 +#define GSM48_IE_CHANMODE_6 0x16 +#define GSM48_IE_CHANMODE_7 0x17 +#define GSM48_IE_CHANMODE_8 0x18 +#define GSM48_IE_CHANDESC_2 0x64 +/* FIXME */ + +/* Section 10.5.4.23 / Table 10.5.130 */ +enum gsm48_signal_val { + GSM48_SIGNAL_DIALTONE = 0x00, + GSM48_SIGNAL_RINGBACK = 0x01, + GSM48_SIGNAL_INTERCEPT = 0x02, + GSM48_SIGNAL_NET_CONG = 0x03, + GSM48_SIGNAL_BUSY = 0x04, + GSM48_SIGNAL_CONFIRM = 0x05, + GSM48_SIGNAL_ANSWER = 0x06, + GSM48_SIGNAL_CALL_WAIT = 0x07, + GSM48_SIGNAL_OFF_HOOK = 0x08, + GSM48_SIGNAL_OFF = 0x3f, + GSM48_SIGNAL_ALERT_OFF = 0x4f, +}; + +enum gsm48_cause_loc { + GSM48_CAUSE_LOC_USER = 0x00, + GSM48_CAUSE_LOC_PRN_S_LU = 0x01, + GSM48_CAUSE_LOC_PUN_S_LU = 0x02, + GSM48_CAUSE_LOC_TRANS_NET = 0x03, + GSM48_CAUSE_LOC_PUN_S_RU = 0x04, + GSM48_CAUSE_LOC_PRN_S_RU = 0x05, + /* not defined */ + GSM48_CAUSE_LOC_INN_NET = 0x07, + GSM48_CAUSE_LOC_NET_BEYOND = 0x0a, +}; + +/* Section 10.5.2.31 RR Cause / Table 10.5.70 */ +enum gsm48_rr_cause { + GSM48_RR_CAUSE_NORMAL = 0x00, + GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01, + GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02, + GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03, + GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04, + GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05, + GSM48_RR_CAUSE_HNDOVER_IMP = 0x06, + GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07, + GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08, + GSM48_RR_CAUSE_CALL_CLEARED = 0x41, + GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f, + GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60, + GSM48_RR_CAUSE_MSG_TYPE_N = 0x61, + GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62, + GSM48_RR_CAUSE_COND_IE_ERROR = 0x64, + GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f, +}; + +/* Section 10.5.4.11 CC Cause / Table 10.5.123 */ +enum gsm48_cc_cause { + GSM48_CC_CAUSE_UNASSIGNED_NR = 1, + GSM48_CC_CAUSE_NO_ROUTE = 3, + GSM48_CC_CAUSE_CHAN_UNACCEPT = 6, + GSM48_CC_CAUSE_OP_DET_BARRING = 8, + GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16, + GSM48_CC_CAUSE_USER_BUSY = 17, + GSM48_CC_CAUSE_USER_NOTRESPOND = 18, + GSM48_CC_CAUSE_USER_ALERTING_NA = 19, + GSM48_CC_CAUSE_CALL_REJECTED = 21, + GSM48_CC_CAUSE_NUMBER_CHANGED = 22, + GSM48_CC_CAUSE_PRE_EMPTION = 25, + GSM48_CC_CAUSE_NONSE_USER_CLR = 26, + GSM48_CC_CAUSE_DEST_OOO = 27, + GSM48_CC_CAUSE_INV_NR_FORMAT = 28, + GSM48_CC_CAUSE_FACILITY_REJ = 29, + GSM48_CC_CAUSE_RESP_STATUS_INQ = 30, + GSM48_CC_CAUSE_NORMAL_UNSPEC = 31, + GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34, + GSM48_CC_CAUSE_NETWORK_OOO = 38, + GSM48_CC_CAUSE_TEMP_FAILURE = 41, + GSM48_CC_CAUSE_SWITCH_CONG = 42, + GSM48_CC_CAUSE_ACC_INF_DISCARD = 43, + GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47, + GSM48_CC_CAUSE_QOS_UNAVAIL = 49, + GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50, + GSM48_CC_CAUSE_INC_BARRED_CUG = 55, + GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58, + GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63, + GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65, + GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68, + GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69, + GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79, + GSM48_CC_CAUSE_INVAL_TRANS_ID = 81, + GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87, + GSM48_CC_CAUSE_INCOMPAT_DEST = 88, + GSM48_CC_CAUSE_INVAL_TRANS_NET = 91, + GSM48_CC_CAUSE_SEMANTIC_INCORR = 95, + GSM48_CC_CAUSE_INVAL_MAND_INF = 96, + GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98, + GSM48_CC_CAUSE_IE_NOTEXIST = 99, + GSM48_CC_CAUSE_COND_IE_ERR = 100, + GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101, + GSM48_CC_CAUSE_RECOVERY_TIMER = 102, + GSM48_CC_CAUSE_PROTO_ERR = 111, + GSM48_CC_CAUSE_INTERWORKING = 127, +}; + +/* Annex G, GSM specific cause values for mobility management */ +enum gsm48_reject_value { + GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2, + GSM48_REJECT_ILLEGAL_MS = 3, + GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4, + GSM48_REJECT_IMEI_NOT_ACCEPTED = 5, + GSM48_REJECT_ILLEGAL_ME = 6, + GSM48_REJECT_PLMN_NOT_ALLOWED = 11, + GSM48_REJECT_LOC_NOT_ALLOWED = 12, + GSM48_REJECT_ROAMING_NOT_ALLOWED = 13, + GSM48_REJECT_NETWORK_FAILURE = 17, + GSM48_REJECT_CONGESTION = 22, + GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32, + GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33, + GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34, + GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38, + GSM48_REJECT_INCORRECT_MESSAGE = 95, + GSM48_REJECT_INVALID_MANDANTORY_INF = 96, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98, + GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99, + GSM48_REJECT_CONDTIONAL_IE_ERROR = 100, + GSM48_REJECT_MSG_NOT_COMPATIBLE = 101, + GSM48_REJECT_PROTOCOL_ERROR = 111, + + /* according to G.6 Additional cause codes for GMM */ + GSM48_REJECT_GPRS_NOT_ALLOWED = 7, + GSM48_REJECT_SERVICES_NOT_ALLOWED = 8, + GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9, + GSM48_REJECT_IMPLICITLY_DETACHED = 10, + GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14, + GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16, +}; + +enum chreq_type { + CHREQ_T_EMERG_CALL, + CHREQ_T_CALL_REEST_TCH_F, + CHREQ_T_CALL_REEST_TCH_H, + CHREQ_T_CALL_REEST_TCH_H_DBL, + CHREQ_T_SDCCH, + CHREQ_T_TCH_F, + CHREQ_T_VOICE_CALL_TCH_H, + CHREQ_T_DATA_CALL_TCH_H, + CHREQ_T_LOCATION_UPD, + CHREQ_T_PAG_R_ANY_NECI0, + CHREQ_T_PAG_R_ANY_NECI1, + CHREQ_T_PAG_R_TCH_F, + CHREQ_T_PAG_R_TCH_FH, + CHREQ_T_LMU, + CHREQ_T_RESERVED_SDCCH, + CHREQ_T_RESERVED_IGNORE, +}; + +/* Chapter 11.3 */ +#define GSM48_T301 180, 0 +#define GSM48_T303 30, 0 +#define GSM48_T305 30, 0 +#define GSM48_T306 30, 0 +#define GSM48_T308 10, 0 +#define GSM48_T310 180, 0 +#define GSM48_T313 30, 0 +#define GSM48_T323 30, 0 +#define GSM48_T331 30, 0 +#define GSM48_T333 30, 0 +#define GSM48_T334 25, 0 /* min 15 */ +#define GSM48_T338 30, 0 + +/* Chapter 5.1.2.2 */ +#define GSM_CSTATE_NULL 0 +#define GSM_CSTATE_INITIATED 1 +#define GSM_CSTATE_MO_CALL_PROC 3 +#define GSM_CSTATE_CALL_DELIVERED 4 +#define GSM_CSTATE_CALL_PRESENT 6 +#define GSM_CSTATE_CALL_RECEIVED 7 +#define GSM_CSTATE_CONNECT_REQUEST 8 +#define GSM_CSTATE_MO_TERM_CALL_CONF 9 +#define GSM_CSTATE_ACTIVE 10 +#define GSM_CSTATE_DISCONNECT_REQ 12 +#define GSM_CSTATE_DISCONNECT_IND 12 +#define GSM_CSTATE_RELEASE_REQ 19 +#define GSM_CSTATE_MO_ORIG_MODIFY 26 +#define GSM_CSTATE_MO_TERM_MODIFY 27 +#define GSM_CSTATE_CONNECT_IND 28 + +#define SBIT(a) (1 << a) +#define ALL_STATES 0xffffffff + +/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */ +#define GSM_LAC_RESERVED_DETACHED 0x0 +#define GSM_LAC_RESERVED_ALL_BTS 0xfffe + +/* GSM 04.08 Bearer Capability: Information Transfer Capability */ +enum gsm48_bcap_itcap { + GSM48_BCAP_ITCAP_SPEECH = 0, + GSM48_BCAP_ITCAP_UNR_DIG_INF = 1, + GSM48_BCAP_ITCAP_3k1_AUDIO = 2, + GSM48_BCAP_ITCAP_FAX_G3 = 3, + GSM48_BCAP_ITCAP_OTHER = 5, + GSM48_BCAP_ITCAP_RESERVED = 7, +}; + +/* GSM 04.08 Bearer Capability: Transfer Mode */ +enum gsm48_bcap_tmod { + GSM48_BCAP_TMOD_CIRCUIT = 0, + GSM48_BCAP_TMOD_PACKET = 1, +}; + +/* GSM 04.08 Bearer Capability: Coding Standard */ +enum gsm48_bcap_coding { + GSM48_BCAP_CODING_GSM_STD = 0, +}; + +/* GSM 04.08 Bearer Capability: Radio Channel Requirements */ +enum gsm48_bcap_rrq { + GSM48_BCAP_RRQ_FR_ONLY = 1, + GSM48_BCAP_RRQ_DUAL_HR = 2, + GSM48_BCAP_RRQ_DUAL_FR = 3, +}; + + +#define GSM48_TMSI_LEN 5 +#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) +#define GSM48_MI_SIZE 32 + + +struct msgb; +struct gsm_bts; +struct gsm_subscriber; +struct gsm_network; +struct gsm_trans; + +/* config options controlling the behaviour of the lower leves */ +void gsm0408_allow_everyone(int allow); + +int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id); +void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, + u_int16_t mnc, u_int16_t lac); +enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); +enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); + +int gsm48_tx_mm_info(struct gsm_lchan *lchan); +int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq); +int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan); +struct msgb *gsm48_msgb_alloc(void); +int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans); +int gsm48_generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi); +int gsm48_generate_mid_from_imsi(u_int8_t *buf, const char* imsi); +int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len); + +int gsm48_send_rr_release(struct gsm_lchan *lchan); +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); +int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id, + u_int8_t apdu_len, const u_int8_t *apdu); +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_class); +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + u_int8_t power_command, u_int8_t ho_ref); + +int bsc_upqueue(struct gsm_network *net); + +int mncc_send(struct gsm_network *net, int msg_type, void *arg); + +/* convert a ASCII phone number to call-control BCD */ +int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, + int h_len, const char *input); +int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv, + int h_len); + +extern const char *gsm0408_cc_msg_names[]; + +int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv); +int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type); +int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr); + +int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode); +int gsm48_rx_rr_modif_ack(struct msgb *msg); +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); + + +#endif diff --git a/src/host/libosmocom/include/osmocom/gsmtap.h b/src/host/libosmocom/include/osmocom/gsmtap.h new file mode 100644 index 000000000..316e73d13 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/gsmtap.h @@ -0,0 +1,60 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload*/ + +#include + +#define GSMTAP_VERSION 0x01 + +#define GSMTAP_TYPE_UM 0x01 +#define GSMTAP_TYPE_ABIS 0x02 +#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ + +#define GSMTAP_BURST_UNKNOWN 0x00 +#define GSMTAP_BURST_FCCH 0x01 +#define GSMTAP_BURST_PARTIAL_SCH 0x02 +#define GSMTAP_BURST_SCH 0x03 +#define GSMTAP_BURST_CTS_SCH 0x04 +#define GSMTAP_BURST_COMPACT_SCH 0x05 +#define GSMTAP_BURST_NORMAL 0x06 +#define GSMTAP_BURST_DUMMY 0x07 +#define GSMTAP_BURST_ACCESS 0x08 +#define GSMTAP_BURST_NONE 0x09 + +struct gsmtap_hdr { + u_int8_t version; /* version, set to 0x01 currently */ + u_int8_t hdr_len; /* length in number of 32bit words */ + u_int8_t type; /* see GSMTAP_TYPE_* */ + u_int8_t timeslot; /* timeslot (0..7 on Um) */ + + u_int16_t arfcn; /* ARFCN (frequency) */ + u_int8_t noise_db; /* noise figure in dB */ + u_int8_t signal_db; /* signal level in dB */ + + u_int32_t frame_number; /* GSM Frame Number (FN) */ + + u_int8_t burst_type; /* Type of burst, see above */ + u_int8_t antenna_nr; /* Antenna Number */ + u_int16_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + + +/* PCAP related definitions */ +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#ifndef LINKTYPE_GSMTAP +#define LINKTYPE_GSMTAP 2342 +#endif +struct pcap_timeval { + int32_t tv_sec; + int32_t tv_usec; +}; + +struct pcap_sf_pkthdr { + struct pcap_timeval ts; /* time stamp */ + u_int32_t caplen; /* lenght of portion present */ + u_int32_t len; /* length of this packet */ +}; + +#endif /* _GSMTAP_H */ diff --git a/src/host/libosmocom/include/osmocom/linuxlist.h b/src/host/libosmocom/include/osmocom/linuxlist.h new file mode 100644 index 000000000..6b77a31dd --- /dev/null +++ b/src/host/libosmocom/include/osmocom/linuxlist.h @@ -0,0 +1,360 @@ +#ifndef _LINUX_LLIST_H +#define _LINUX_LLIST_H + +#include + +#ifndef inline +#define inline __inline__ +#endif + +static inline void prefetch(const void *x) {;} + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked llist implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct llist_head { + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: llist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/** + * llist_add_tail - add a new entry + * @new: new entry to be added + * @head: llist head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * llist_del - deletes entry from llist. + * @entry: the element to delete from the llist. + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/** + * llist_del_init - deletes entry from llist and reinitialize it. + * @entry: the element to delete from the llist. + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/** + * llist_move - delete from one llist and add as another's head + * @llist: the entry to move + * @head: the head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/** + * llist_move_tail - delete from one llist and add as another's tail + * @llist: the entry to move + * @head: the head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/** + * llist_empty - tests whether a llist is empty + * @head: the llist to test. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * llist_splice - join two llists + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/** + * llist_splice_init - join two llists and reinitialise the emptied llist. + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/** + * llist_entry - get the struct for this entry + * @ptr: the &struct llist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_struct within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * llist_for_each_prev - iterate over a llist backwards + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * llist_for_each_safe - iterate over a llist safe against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * llist_for_each_entry - iterate over llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_reverse - iterate backwards over llist of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * llist_for_each_entry_continue - iterate over llist of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + + +#endif diff --git a/src/host/libosmocom/include/osmocom/meas_rep.h b/src/host/libosmocom/include/osmocom/meas_rep.h new file mode 100644 index 000000000..3c2c8d1c1 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/meas_rep.h @@ -0,0 +1,85 @@ +#ifndef _MEAS_REP_H +#define _MEAS_REP_H + +#define MRC_F_PROCESSED 0x0001 + +/* extracted from a L3 measurement report IE */ +struct gsm_meas_rep_cell { + u_int8_t rxlev; + u_int8_t bsic; + u_int8_t neigh_idx; + u_int16_t arfcn; + unsigned int flags; +}; + +/* RX Level and RX Quality */ +struct gsm_rx_lev_qual { + u_int8_t rx_lev; + u_int8_t rx_qual; +}; + +/* unidirectional measumrement report */ +struct gsm_meas_rep_unidir { + struct gsm_rx_lev_qual full; + struct gsm_rx_lev_qual sub; +}; + +#define MEAS_REP_F_UL_DTX 0x01 +#define MEAS_REP_F_DL_VALID 0x02 +#define MEAS_REP_F_BA1 0x04 +#define MEAS_REP_F_DL_DTX 0x08 +#define MEAS_REP_F_MS_TO 0x10 +#define MEAS_REP_F_MS_L1 0x20 +#define MEAS_REP_F_FPC 0x40 + +/* parsed uplink and downlink measurement result */ +struct gsm_meas_rep { + /* back-pointer to the logical channel */ + struct gsm_lchan *lchan; + + /* number of the measurement report */ + u_int8_t nr; + /* flags, see MEAS_REP_F_* */ + unsigned int flags; + + /* uplink and downlink rxlev, rxqual; full and sub */ + struct gsm_meas_rep_unidir ul; + struct gsm_meas_rep_unidir dl; + + u_int8_t bs_power; + u_int8_t ms_timing_offset; + struct { + int8_t pwr; /* MS power in dBm */ + u_int8_t ta; /* MS timing advance */ + } ms_l1; + + /* neighbor measurement reports for up to 6 cells */ + int num_cell; + struct gsm_meas_rep_cell cell[6]; +}; + +enum meas_rep_field { + MEAS_REP_DL_RXLEV_FULL, + MEAS_REP_DL_RXLEV_SUB, + MEAS_REP_DL_RXQUAL_FULL, + MEAS_REP_DL_RXQUAL_SUB, + MEAS_REP_UL_RXLEV_FULL, + MEAS_REP_UL_RXLEV_SUB, + MEAS_REP_UL_RXQUAL_FULL, + MEAS_REP_UL_RXQUAL_SUB, +}; + +/* obtain an average over the last 'num' fields in the meas reps */ +int get_meas_rep_avg(const struct gsm_lchan *lchan, + enum meas_rep_field field, unsigned int num); + +/* Check if N out of M last values for FIELD are >= bd */ +int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, + enum meas_rep_field field, + unsigned int n, unsigned int m, int be); + +unsigned int calc_initial_idx(unsigned int array_size, + unsigned int meas_rep_idx, + unsigned int num_values); + +#endif /* _MEAS_REP_H */ diff --git a/src/host/libosmocom/include/osmocom/msgb.h b/src/host/libosmocom/include/osmocom/msgb.h new file mode 100644 index 000000000..d0efc90b5 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/msgb.h @@ -0,0 +1,114 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008 by Harald Welte + * 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 +#include "linuxlist.h" + +struct bts_link; + +struct msgb { + struct llist_head list; + + /* ptr to the physical E1 link to the BTS(s) */ + struct gsm_bts_link *bts_link; + + /* Part of which TRX logical channel we were received / transmitted */ + struct gsm_bts_trx *trx; + struct gsm_lchan *lchan; + + unsigned char *l2h; + unsigned char *l3h; + unsigned char *smsh; + + u_int16_t data_len; + u_int16_t len; + + unsigned char *head; + unsigned char *tail; + unsigned char *data; + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(u_int16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_sms(m) ((void *)(m->smsh)) + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (u_int8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (u_int8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->data + msgb->data_len) - msgb->tail; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/host/libosmocom/include/osmocom/select.h b/src/host/libosmocom/include/osmocom/select.h new file mode 100644 index 000000000..2d8b3ec07 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/select.h @@ -0,0 +1,22 @@ +#ifndef _BSC_SELECT_H +#define _BSC_SELECT_H + +#include "linuxlist.h" + +#define BSC_FD_READ 0x0001 +#define BSC_FD_WRITE 0x0002 +#define BSC_FD_EXCEPT 0x0004 + +struct bsc_fd { + struct llist_head list; + int fd; + unsigned int when; + int (*cb)(struct bsc_fd *fd, unsigned int what); + void *data; + unsigned int priv_nr; +}; + +int bsc_register_fd(struct bsc_fd *fd); +void bsc_unregister_fd(struct bsc_fd *fd); +int bsc_select_main(int polling); +#endif /* _BSC_SELECT_H */ diff --git a/src/host/libosmocom/include/osmocom/talloc.h b/src/host/libosmocom/include/osmocom/talloc.h new file mode 100644 index 000000000..1c243b80d --- /dev/null +++ b/src/host/libosmocom/include/osmocom/talloc.h @@ -0,0 +1,192 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +#define HAVE_VA_COPY + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) +#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void *talloc_pool(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +char *talloc_strdup(const void *t, const char *p); +char *talloc_strdup_append(char *s, const char *a); +char *talloc_strdup_append_buffer(char *s, const char *a); + +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_strndup_append(char *s, const char *a, size_t n); +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); + +#endif diff --git a/src/host/libosmocom/include/osmocom/timer.h b/src/host/libosmocom/include/osmocom/timer.h new file mode 100644 index 000000000..fee888bfd --- /dev/null +++ b/src/host/libosmocom/include/osmocom/timer.h @@ -0,0 +1,72 @@ +/* + * (C) 2008, 2009 by Holger Hans Peter Freyther + * 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. + * + */ + +#ifndef TIMER_H +#define TIMER_H + +#include + +#include "linuxlist.h" + +/** + * Timer management: + * - Create a struct timer_list + * - Fill out timeout and use add_timer or + * use schedule_timer to schedule a timer in + * x seconds and microseconds from now... + * - Use del_timer to remove the timer + * + * Internally: + * - We hook into select.c to give a timeval of the + * nearest timer. On already passed timers we give + * it a 0 to immediately fire after the select + * - update_timers will call the callbacks and remove + * the timers. + * + */ +struct timer_list { + struct llist_head entry; + struct timeval timeout; + unsigned int active : 1; + unsigned int handled : 1; + unsigned int in_list : 1; + + void (*cb)(void*); + void *data; +}; + +/** + * timer management + */ +void bsc_add_timer(struct timer_list *timer); +void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds); +void bsc_del_timer(struct timer_list *timer); +int bsc_timer_pending(struct timer_list *timer); + + +/** + * internal timer list management + */ +struct timeval *bsc_nearest_timer(); +void bsc_prepare_timers(); +int bsc_update_timers(); +int bsc_timer_check(void); + +#endif diff --git a/src/host/libosmocom/src/debug.c b/src/host/libosmocom/src/debug.c new file mode 100644 index 000000000..5dc2b2256 --- /dev/null +++ b/src/host/libosmocom/src/debug.c @@ -0,0 +1,463 @@ +/* Debugging/Logging support code */ +/* (C) 2008 by Harald Welte + * (C) 2008 by Holger Hans Peter Freyther + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +struct value_string { + unsigned int value; + const char *str; +}; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +struct gsm_bts; +struct gsm_subscriber; +struct gsm_lchan; + +/* default categories */ +static struct debug_category default_categories[Debug_LastEntry] = { + [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMM] = { .enabled = 1, .loglevel = LOGL_INFO }, + [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE }, +}; + +const char *get_value_string(const struct value_string *vs, u_int32_t val) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (vs[i].value == val) + return vs[i].str; + } + return "unknown"; +} + +int get_string_value(const struct value_string *vs, const char *str) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (!strcasecmp(vs[i].str, str)) + return vs[i].value; + } + return -EINVAL; +} + +struct debug_info { + const char *name; + const char *color; + const char *description; + int number; + int position; +}; + +struct debug_context { + struct gsm_lchan *lchan; + struct gsm_subscriber *subscr; + struct gsm_bts *bts; +}; + +static struct debug_context debug_context; +static void *tall_dbg_ctx = NULL; +static LLIST_HEAD(target_list); + +#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ + { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, + +static const struct debug_info debug_info[] = { + DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") + DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") + DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "") + DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "") + DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "") + DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "") + DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "") + DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "") + DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "") + DEBUG_CATEGORY(DINP, "DINP", "", "") + DEBUG_CATEGORY(DMI, "DMI", "", "") + DEBUG_CATEGORY(DMIB, "DMIB", "", "") + DEBUG_CATEGORY(DMUX, "DMUX", "", "") + DEBUG_CATEGORY(DMEAS, "DMEAS", "", "") + DEBUG_CATEGORY(DSCCP, "DSCCP", "", "") + DEBUG_CATEGORY(DMSC, "DMSC", "", "") + DEBUG_CATEGORY(DMGCP, "DMGCP", "", "") + DEBUG_CATEGORY(DHO, "DHO", "", "") + DEBUG_CATEGORY(DDB, "DDB", "", "") + DEBUG_CATEGORY(DDB, "DREF", "", "") +}; + +static const struct value_string loglevel_strs[] = { + { 0, "EVERYTHING" }, + { 1, "DEBUG" }, + { 3, "INFO" }, + { 5, "NOTICE" }, + { 7, "ERROR" }, + { 8, "FATAL" }, + { 0, NULL }, +}; + +int debug_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +int debug_parse_category(const char *category) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (!strcasecmp(debug_info[i].name+1, category)) + return debug_info[i].number; + } + + return -EINVAL; +} + +/* + * Parse the category mask. + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void debug_parse_category_mask(struct debug_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + + category_token = strtok(mask, ":"); + do { + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(debug_info[i].name, category_token, length) == 0) { + int number = debug_info[i].number; + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[number].enabled = 1; + target->categories[number].loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (debug_info[i].number == subsys) + return debug_info[i].color; + } + + return ""; +} + +static void _output(struct debug_target *target, unsigned int subsys, char *file, int line, + int cont, const char *format, va_list ap) +{ + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; + + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; + + /* are we using color */ + if (target->use_color) { + snprintf(col, sizeof(col), "%s", color(subsys)); + col[sizeof(col)-1] = '\0'; + } + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; + + if (!cont) { + if (target->print_timestamp) { + char *timestr; + time_t tm; + tm = time(NULL); + timestr = ctime(&tm); + timestr[strlen(timestr)-1] = '\0'; + snprintf(tim, sizeof(tim), "%s ", timestr); + tim[sizeof(tim)-1] = '\0'; + } + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); + sub[sizeof(sub)-1] = '\0'; + } + + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + final[sizeof(final)-1] = '\0'; + target->output(target, final); +} + + +static void _debugp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct debug_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct debug_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be debugged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (category->loglevel != 0 && level < category->loglevel) + continue; + + /* + * Apply filters here... if that becomes messy we will need to put + * filters in a list and each filter will say stop, continue, output + */ + if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) { + output = 1; +#if 0 + } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0 + && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) { + output = 1; +#endif + } + + if (output) { + /* FIXME: copying the va_list is an ugly workaround against a bug + * hidden somewhere in _output. If we do not copy here, the first + * call to _output() will corrupt the va_list contents, and any + * further _output() calls with the same va_list will segfault */ + va_list bp; + va_copy(bp, ap); + _output(tar, subsys, file, line, cont, format, bp); + va_end(bp); + } + } +} + +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, level, file, line, cont, format, ap); + va_end(ap); +} + +static char hexd_buff[4096]; + +char *hexdump(const unsigned char *buf, int len) +{ + int i; + char *cur = hexd_buff; + + hexd_buff[0] = 0; + for (i = 0; i < len; i++) { + int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); + int rc = snprintf(cur, len_remain, "%02x ", buf[i]); + if (rc <= 0) + break; + cur += rc; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + + + +void debug_add_target(struct debug_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void debug_del_target(struct debug_target *target) +{ + llist_del(&target->entry); +} + +void debug_reset_context(void) +{ + memset(&debug_context, 0, sizeof(debug_context)); +} + +/* currently we are not reffing these */ +void debug_set_context(int ctx, void *value) +{ + switch (ctx) { + case BSC_CTX_LCHAN: + debug_context.lchan = (struct gsm_lchan *) value; + break; + case BSC_CTX_SUBSCR: + debug_context.subscr = (struct gsm_subscriber *) value; + break; + case BSC_CTX_BTS: + debug_context.bts = (struct gsm_bts *) value; + break; + case BSC_CTX_SCCP: + break; + default: + break; + } +} + +void debug_set_imsi_filter(struct debug_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= DEBUG_FILTER_IMSI; + target->imsi_filter = talloc_strdup(target, imsi); + } else if (target->imsi_filter) { + target->filter_map &= ~DEBUG_FILTER_IMSI; + talloc_free(target->imsi_filter); + target->imsi_filter = NULL; + } +} + +void debug_set_all_filter(struct debug_target *target, int all) +{ + if (all) + target->filter_map |= DEBUG_FILTER_ALL; + else + target->filter_map &= ~DEBUG_FILTER_ALL; +} + +void debug_set_use_color(struct debug_target *target, int use_color) +{ + target->use_color = use_color; +} + +void debug_set_print_timestamp(struct debug_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void debug_set_log_level(struct debug_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level) +{ + if (category >= Debug_LastEntry) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct debug_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct debug_target *debug_target_create(void) +{ + struct debug_target *target; + + target = talloc_zero(tall_dbg_ctx, struct debug_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, default_categories, sizeof(default_categories)); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct debug_target *debug_target_create_stderr(void) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void debug_init(void) +{ + tall_dbg_ctx = talloc_named_const(NULL, 1, "debug"); +} diff --git a/src/host/libosmocom/src/msgb.c b/src/host/libosmocom/src/msgb.c new file mode 100644 index 000000000..2c7941255 --- /dev/null +++ b/src/host/libosmocom/src/msgb.c @@ -0,0 +1,97 @@ +/* (C) 2008 by Harald Welte + * 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 +#include +#include +#include + +#include +#include +#include + +void *tall_msgb_ctx; + +struct msgb *msgb_alloc(u_int16_t size, const char *name) +{ + struct msgb *msg; + + msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); + + if (!msg) { + LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n"); + return NULL; + } + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + //msg->end = msg->tail + size; + + return msg; +} + +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct msgb, list); +} + +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + /* reset pointers */ + msg->bts_link = NULL; + msg->trx = NULL; + msg->lchan = NULL; + msg->l2h = NULL; + msg->l3h = NULL; + msg->smsh = NULL; +} diff --git a/src/host/libosmocom/src/select.c b/src/host/libosmocom/src/select.c new file mode 100644 index 000000000..c212cc79b --- /dev/null +++ b/src/host/libosmocom/src/select.c @@ -0,0 +1,124 @@ +/* select filedescriptor handling, taken from: + * userspace logging daemon for the iptables ULOG target + * of the linux 2.4 netfilter subsystem. + * + * (C) 2000-2009 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +static int maxfd = 0; +static LLIST_HEAD(bsc_fds); +static int unregistered_count; + +int bsc_register_fd(struct bsc_fd *fd) +{ + int flags; + + /* make FD nonblocking */ + flags = fcntl(fd->fd, F_GETFL); + if (flags < 0) + return flags; + flags |= O_NONBLOCK; + flags = fcntl(fd->fd, F_SETFL, flags); + if (flags < 0) + return flags; + + /* Register FD */ + if (fd->fd > maxfd) + maxfd = fd->fd; + + llist_add_tail(&fd->list, &bsc_fds); + + return 0; +} + +void bsc_unregister_fd(struct bsc_fd *fd) +{ + unregistered_count++; + llist_del(&fd->list); +} + +int bsc_select_main(int polling) +{ + struct bsc_fd *ufd, *tmp; + fd_set readset, writeset, exceptset; + int work = 0, rc; + struct timeval no_time = {0, 0}; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + /* prepare read and write fdsets */ + llist_for_each_entry(ufd, &bsc_fds, list) { + if (ufd->when & BSC_FD_READ) + FD_SET(ufd->fd, &readset); + + if (ufd->when & BSC_FD_WRITE) + FD_SET(ufd->fd, &writeset); + + if (ufd->when & BSC_FD_EXCEPT) + FD_SET(ufd->fd, &exceptset); + } + + bsc_timer_check(); + + if (!polling) + bsc_prepare_timers(); + rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer()); + if (rc < 0) + return 0; + + /* fire timers */ + bsc_update_timers(); + + /* call registered callback functions */ +restart: + unregistered_count = 0; + llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) { + int flags = 0; + + if (FD_ISSET(ufd->fd, &readset)) { + flags |= BSC_FD_READ; + FD_CLR(ufd->fd, &readset); + } + + if (FD_ISSET(ufd->fd, &writeset)) { + flags |= BSC_FD_WRITE; + FD_CLR(ufd->fd, &writeset); + } + + if (FD_ISSET(ufd->fd, &exceptset)) { + flags |= BSC_FD_EXCEPT; + FD_CLR(ufd->fd, &exceptset); + } + + if (flags) { + work = 1; + ufd->cb(ufd, flags); + } + /* ugly, ugly hack. If more than one filedescriptors were + * unregistered, they might have been consecutive and + * llist_for_each_entry_safe() is no longer safe */ + if (unregistered_count > 1) + goto restart; + } + return work; +} diff --git a/src/host/libosmocom/src/signal.c b/src/host/libosmocom/src/signal.c new file mode 100644 index 000000000..d45e94e68 --- /dev/null +++ b/src/host/libosmocom/src/signal.c @@ -0,0 +1,83 @@ +/* Generic signalling/notification infrastructure */ +/* (C) 2009 by Holger Hans Peter Freyther + * 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 +#include +#include +#include + + +void *tall_sigh_ctx; +static LLIST_HEAD(signal_handler_list); + +struct signal_handler { + struct llist_head entry; + unsigned int subsys; + signal_cbfn *cbfn; + void *data; +}; + + +int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) +{ + struct signal_handler *sig_data; + + sig_data = talloc(tall_sigh_ctx, struct signal_handler); + if (!sig_data) + return -ENOMEM; + + memset(sig_data, 0, sizeof(*sig_data)); + + sig_data->subsys = subsys; + sig_data->data = data; + sig_data->cbfn = cbfn; + + /* FIXME: check if we already have a handler for this subsys/cbfn/data */ + + llist_add_tail(&sig_data->entry, &signal_handler_list); + + return 0; +} + +void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->cbfn == cbfn && handler->data == data + && subsys == handler->subsys) { + llist_del(&handler->entry); + talloc_free(handler); + break; + } + } +} + + +void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->subsys != subsys) + continue; + (*handler->cbfn)(subsys, signal, handler->data, signal_data); + } +} diff --git a/src/host/libosmocom/src/talloc.c b/src/host/libosmocom/src/talloc.c new file mode 100644 index 000000000..5b8bc274f --- /dev/null +++ b/src/host/libosmocom/src/talloc.c @@ -0,0 +1,1805 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#ifdef _SAMBA_BUILD_ +#include "version.h" +#if (SAMBA_VERSION_MAJOR<4) +#include "includes.h" +/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file + * we trust ourselves... */ +#ifdef malloc +#undef malloc +#endif +#ifdef realloc +#undef realloc +#endif +#define _TALLOC_SAMBA3 +#endif /* (SAMBA_VERSION_MAJOR<4) */ +#endif /* _SAMBA_BUILD_ */ + +#ifndef _TALLOC_SAMBA3 +//#include "replace.h" +#include +#include +#include +#include +#define __USE_GNU +#include +#undef __USE_GNU +#include +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif /* not _TALLOC_SAMBA3 */ + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ +#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +#ifdef __APPLE__ +/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */ +size_t strnlen(const char *s, size_t n) +{ + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); +} +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + unsigned flags; + + /* + * "pool" has dual use: + * + * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" + * marks the end of the currently allocated area. + * + * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" + * is a pointer to the struct talloc_chunk of the pool that it was + * allocated from. This way children can quickly find the pool to chew + * from. + */ + void *pool; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +static void (*talloc_abort_fn)(const char *reason); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + talloc_abort_fn = abort_fn; +} + +static void talloc_abort(const char *reason) +{ + if (!talloc_abort_fn) { + TALLOC_ABORT(reason); + } + + talloc_abort_fn(reason); +} + +static void talloc_abort_double_free(void) +{ + talloc_abort("Bad talloc magic value - double free"); +} + +static void talloc_abort_unknown_value(void) +{ + talloc_abort("Bad talloc magic value - unknown value"); +} + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + talloc_abort_double_free(); + } else { + talloc_abort_unknown_value(); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + A pool carries an in-pool object count count in the first 16 bytes. + bytes. This is done to support talloc_steal() to a parent outside of the + pool. The count includes the pool itself, so a talloc_free() on a pool will + only destroy the pool if the count has dropped to zero. A talloc_free() of a + pool member will reduce the count, and eventually also call free(3) on the + pool memory. + + The object count is not put into "struct talloc_chunk" because it is only + relevant for talloc pools and the alignment to 16 bytes would increase the + memory footprint of each talloc chunk by those 16 bytes. +*/ + +#define TALLOC_POOL_HDR_SIZE 16 + +static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) +{ + return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); +} + +/* + Allocate from a pool +*/ + +static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, + size_t size) +{ + struct talloc_chunk *pool_ctx = NULL; + size_t space_left; + struct talloc_chunk *result; + size_t chunk_size; + + if (parent == NULL) { + return NULL; + } + + if (parent->flags & TALLOC_FLAG_POOL) { + pool_ctx = parent; + } + else if (parent->flags & TALLOC_FLAG_POOLMEM) { + pool_ctx = (struct talloc_chunk *)parent->pool; + } + + if (pool_ctx == NULL) { + return NULL; + } + + space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) + - ((char *)pool_ctx->pool); + + /* + * Align size to 16 bytes + */ + chunk_size = ((size + 15) & ~15); + + if (space_left < chunk_size) { + return NULL; + } + + result = (struct talloc_chunk *)pool_ctx->pool; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(result, size); +#endif + + pool_ctx->pool = (void *)((char *)result + chunk_size); + + result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; + result->pool = pool_ctx; + + *talloc_pool_objectcount(pool_ctx) += 1; + + return result; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc = NULL; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + if (context != NULL) { + tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), + TC_HDR_SIZE+size); + } + + if (tc == NULL) { + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + tc->flags = TALLOC_MAGIC; + tc->pool = NULL; + } + + tc->size = size; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + * Create a talloc pool + */ + +void *talloc_pool(const void *context, size_t size) +{ + void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); + struct talloc_chunk *tc; + + if (unlikely(result == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(result); + + tc->flags |= TALLOC_FLAG_POOL; + tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; + + *talloc_pool_objectcount(tc) = 1; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); +#endif + + return result; +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + + if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { + struct talloc_chunk *pool; + unsigned int *pool_object_count; + + pool = (tc->flags & TALLOC_FLAG_POOL) + ? tc : (struct talloc_chunk *)tc->pool; + + pool_object_count = talloc_pool_objectcount(pool); + + if (*pool_object_count == 0) { + talloc_abort("Pool object count zero!"); + } + + *pool_object_count -= 1; + + if (*pool_object_count == 0) { + free(pool); + } + } + else { + free(tc); + } + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + +static void talloc_abort_type_missmatch(const char *location, + const char *name, + const char *expected) +{ + const char *reason; + + reason = talloc_asprintf(NULL, + "%s: Type mismatch: name[%s] expected[%s]", + location, + name?name:"NULL", + expected); + if (!reason) { + reason = "Type mismatch"; + } + + talloc_abort(reason); +} + +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) +{ + const char *pname; + + if (unlikely(ptr == NULL)) { + talloc_abort_type_missmatch(location, NULL, name); + return NULL; + } + + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + + talloc_abort_type_missmatch(location, pname, name); + return NULL; +} + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + if ((tc->flags & TALLOC_FLAG_POOL) + && (*talloc_pool_objectcount(tc) == 1)) { + tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS( + tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); +#endif + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + return _talloc_free(ptr); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + bool malloced = false; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* don't let anybody try to realloc a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { + return NULL; + } + + /* don't shrink if we have less than 1k to gain */ + if ((size < tc->size) && ((tc->size - size) < 1024)) { + tc->size = size; + return ptr; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + if (tc->flags & TALLOC_FLAG_POOLMEM) { + + new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); + *talloc_pool_objectcount((struct talloc_chunk *) + (tc->pool)) -= 1; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + } + } + else { + new_ptr = realloc(tc, size + TC_HDR_SIZE); + } +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (malloced) { + tc->flags &= ~TALLOC_FLAG_POOLMEM; + } + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) +{ + char *ret; + + ret = (char *)__talloc(t, len + 1); + if (unlikely(!ret)) return NULL; + + memcpy(ret, p, len); + ret[len] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strlen(p)); +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strnlen(p, n)); +} + +static inline char *__talloc_strlendup_append(char *s, size_t slen, + const char *a, size_t alen) +{ + char *ret; + + ret = talloc_realloc(NULL, s, char, slen + alen + 1); + if (unlikely(!ret)) return NULL; + + /* append the string and the trailing \0 */ + memcpy(&ret[slen], a, alen); + ret[slen+alen] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + * Appends at the end of the string. + */ +char *talloc_strdup_append(char *s, const char *a) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strdup_append_buffer(char *s, const char *a) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strlen(a)); +} + +/* + * Appends at the end of the string. + */ +char *talloc_strndup_append(char *s, const char *a, size_t n) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); +} + +#ifndef HAVE_VA_COPY +#ifdef HAVE___VA_COPY +#define va_copy(dest, src) __va_copy(dest, src) +#else +#define va_copy(dest, src) (dest) = (src) +#endif +#endif + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + char c; + + /* this call looks strange, but it makes it work on older solaris boxes */ + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + if (unlikely(len < 0)) { + return NULL; + } + + ret = (char *)__talloc(t, len+1); + if (unlikely(!ret)) return NULL; + + va_copy(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(ret, ret); + return ret; +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(3,0); + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) +{ + ssize_t alen; + va_list ap2; + char c; + + va_copy(ap2, ap); + alen = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (alen <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, slen + alen + 1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s + slen, alen + 1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(s, s); + return s; +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. Appends at the end + * of the string. + **/ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Always appends at the + * end of the talloc'ed buffer, not the end of the string. + **/ +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_vaslenprintf_append(s, slen, fmt, ap); +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a buffer. + */ +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/src/host/libosmocom/src/timer.c b/src/host/libosmocom/src/timer.c new file mode 100644 index 000000000..acb4d923b --- /dev/null +++ b/src/host/libosmocom/src/timer.c @@ -0,0 +1,185 @@ +/* + * (C) 2008,2009 by Holger Hans Peter Freyther + * 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 +#include +#include + +static LLIST_HEAD(timer_list); +static struct timeval s_nearest_time; +static struct timeval s_select_time; + +#define MICRO_SECONDS 1000000LL + +#define TIME_SMALLER(left, right) \ + (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec) + +void bsc_add_timer(struct timer_list *timer) +{ + struct timer_list *list_timer; + + /* TODO: Optimize and remember the closest item... */ + timer->active = 1; + + /* this might be called from within update_timers */ + llist_for_each_entry(list_timer, &timer_list, entry) + if (timer == list_timer) + return; + + timer->in_list = 1; + llist_add(&timer->entry, &timer_list); +} + +void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds) +{ + struct timeval current_time; + + gettimeofday(¤t_time, NULL); + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + currentTime += seconds * MICRO_SECONDS + microseconds; + timer->timeout.tv_sec = currentTime / MICRO_SECONDS; + timer->timeout.tv_usec = currentTime % MICRO_SECONDS; + bsc_add_timer(timer); +} + +void bsc_del_timer(struct timer_list *timer) +{ + if (timer->in_list) { + timer->active = 0; + timer->in_list = 0; + llist_del(&timer->entry); + } +} + +int bsc_timer_pending(struct timer_list *timer) +{ + return timer->active; +} + +/* + * if we have a nearest time return the delta between the current + * time and the time of the nearest timer. + * If the nearest timer timed out return NULL and then we will + * dispatch everything after the select + */ +struct timeval *bsc_nearest_timer() +{ + struct timeval current_time; + + if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) + return NULL; + + if (gettimeofday(¤t_time, NULL) == -1) + return NULL; + + unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + + if (nearestTime < currentTime) { + s_select_time.tv_sec = 0; + s_select_time.tv_usec = 0; + } else { + s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + } + + return &s_select_time; +} + +/* + * Find the nearest time and update s_nearest_time + */ +void bsc_prepare_timers() +{ + struct timer_list *timer, *nearest_timer = NULL; + llist_for_each_entry(timer, &timer_list, entry) { + if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) { + nearest_timer = timer; + } + } + + if (nearest_timer) { + s_nearest_time = nearest_timer->timeout; + } else { + memset(&s_nearest_time, 0, sizeof(struct timeval)); + } +} + +/* + * fire all timers... and remove them + */ +int bsc_update_timers() +{ + struct timeval current_time; + struct timer_list *timer, *tmp; + int work = 0; + + gettimeofday(¤t_time, NULL); + + /* + * The callbacks might mess with our list and in this case + * even llist_for_each_entry_safe is not safe to use. To allow + * del_timer, add_timer, schedule_timer to be called from within + * the callback we jump through some loops. + * + * First we set the handled flag of each active timer to zero, + * then we iterate over the list and execute the callbacks. As the + * list might have been changed (specially the next) from within + * the callback we have to start over again. Once every callback + * is dispatched we will remove the non-active from the list. + * + * TODO: If this is a performance issue we can poison a global + * variable in add_timer and del_timer and only then restart. + */ + llist_for_each_entry(timer, &timer_list, entry) { + timer->handled = 0; + } + +restart: + llist_for_each_entry(timer, &timer_list, entry) { + if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) { + timer->handled = 1; + timer->active = 0; + (*timer->cb)(timer->data); + work = 1; + goto restart; + } + } + + llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { + timer->handled = 0; + if (!timer->active) { + bsc_del_timer(timer); + } + } + + return work; +} + +int bsc_timer_check(void) +{ + struct timer_list *timer; + int i = 0; + + llist_for_each_entry(timer, &timer_list, entry) { + i++; + } + return i; +} diff --git a/src/host/osmocon/.gitignore b/src/host/osmocon/.gitignore new file mode 100644 index 000000000..a9966b121 --- /dev/null +++ b/src/host/osmocon/.gitignore @@ -0,0 +1,9 @@ +*.o +osmocon +*.id* +*.nam +*.til +*.dump +*.bin +*.log +version.h diff --git a/src/host/osmocon/Makefile b/src/host/osmocon/Makefile new file mode 100644 index 000000000..9cb11d2d7 --- /dev/null +++ b/src/host/osmocon/Makefile @@ -0,0 +1,19 @@ +all: osmocon + +OSMOCOM_SRC=../libosmocom/src/select.c ../libosmocom/src/timer.c \ + ../libosmocom/src/msgb.c ../libosmocom/src/talloc.c \ + ../libosmocom/src/debug.c +SERCOMM_SRC=../../target/hello_world/comm/sercomm.c +INCLUDES=-I../libosmocom/include -I../../target/hello_world/include/comm + +.PHONY: version.h +version.h: + @echo -n \#define VERSION \"git- > version.h + @git log --oneline -n1 osmocon.c|cut -d ' ' -f 1 |tr -d '\n' >> version.h + @echo \" >> version.h + +osmocon: version.h $(OSMOCOM_SRC) $(SERCOMM_SRC) + $(CC) $(CFLAGS) -DHOST_BUILD $(INCLUDES) -o $@ $(OSMOCOM_SRC) $(SERCOMM_SRC) osmocon.c + +clean: + @rm -f osmocon version.h diff --git a/src/host/osmocon/memdump_convert.pl b/src/host/osmocon/memdump_convert.pl new file mode 100755 index 000000000..3d18a0b3d --- /dev/null +++ b/src/host/osmocon/memdump_convert.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +my $num_line = 0; +my $num_hex = 0; +my $oldaddr; + +while (my $line = ) { + chomp($line); + $num_line++; + my (@hex) = $line =~ /(\w{8}): (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8})/; + my $addr = hex(shift @hex); + if ($addr != 0 && $addr != $oldaddr + 0x20) { + printf(STDERR "gap of %u between 0x%08x and 0x%08x\n%s\n", + $addr - $oldaddr, $addr, $oldaddr, $line); + } + foreach my $h (@hex) { + $num_hex++; + # poor mans endian conversion + my ($a, $b, $c, $d) = $h =~/(\w\w)(\w\w)(\w\w)(\w\w)/; + my $h_reorder = $d . $c . $b . $a; + # convert into actual binary number + my $tmp = pack('H8', $h_reorder); + syswrite(STDOUT, $tmp, 4); + } + $oldaddr = $addr; +} + +printf(STDERR "num lines/num hex: %u/%u\n", $num_line, $num_hex); + diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c new file mode 100644 index 000000000..094dbeb22 --- /dev/null +++ b/src/host/osmocon/osmocon.c @@ -0,0 +1,660 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "version.h" + +#define MODEM_BAUDRATE B115200 +#define MAX_DNLOAD_SIZE 0xFFFF +#define MAX_HDR_SIZE 128 + +enum dnload_state { + WAITING_PROMPT1, + WAITING_PROMPT2, + DOWNLOADING, +}; + +enum dnload_mode { + MODE_C123, + MODE_C123xor, + MODE_C155, +}; + +struct dnload { + enum dnload_state state; + enum dnload_mode mode; + struct bsc_fd serial_fd; + char *filename; + + int print_hdlc; + + /* data to be downloaded */ + uint8_t *data; + int data_len; + + uint8_t *write_ptr; + + /* sockaddr in */ + struct bsc_fd socket; +}; + +/** + * a connection of the layer2 + */ +struct layer2_connection { + struct llist_head entry; + struct bsc_fd fd; +}; + +static LLIST_HEAD(connections); +static struct dnload dnload; + +static const uint8_t phone_prompt1[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x01, 0x40 }; +static const uint8_t dnload_cmd[] = { 0x1b, 0xf6, 0x02, 0x00, 0x52, 0x01, 0x53 }; +static const uint8_t phone_prompt2[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x02, 0x43 }; +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; +static const uint8_t phone_nack_magic[]= { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x57 }; +static const uint8_t phone_nack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x45, 0x53, 0x16 }; +static const uint8_t ftmtool[] = { "ftmtool" }; + +/* The C123 has a hard-coded check inside the ramloder that requires the following + * bytes to be always the first four bytes of the image */ +static const uint8_t data_hdr_c123[] = { 0xee, 0x4c, 0x9f, 0x63 }; + +/* The C155 doesn't have some strange restriction on what the first four bytes have + * to be, but it starts the ramloader in THUMB mode. We use the following four bytes + * to switch back to ARM mode: + 800100: 4778 bx pc + 800102: 46c0 nop ; (mov r8, r8) + */ +static const uint8_t data_hdr_c155[] = { 0x78, 0x47, 0xc0, 0x46 }; + +static const uint8_t dummy_data[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde }; + +static int serial_init(const char *serial_dev) +{ + struct termios options; + int fd, v24; + + fd = open(serial_dev, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) + return fd; + + fcntl(fd, F_SETFL, 0); + + /* Configure serial interface */ + tcgetattr(fd, &options); + + cfsetispeed(&options, MODEM_BAUDRATE); + cfsetospeed(&options, MODEM_BAUDRATE); + + /* local read */ + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + + /* hardware flow control off */ + options.c_cflag &= ~CRTSCTS; + + /* software flow control off */ + options.c_iflag &= ~(IXON | IXOFF | IXANY); + + /* we want raw i/o */ + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_iflag &= ~(INLCR | ICRNL | IGNCR); + options.c_oflag &= ~(ONLCR); + + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + options.c_cc[VINTR] = 0; + options.c_cc[VQUIT] = 0; + options.c_cc[VSTART] = 0; + options.c_cc[VSTOP] = 0; + options.c_cc[VSUSP] = 0; + + tcsetattr(fd, TCSANOW, &options); + + /* set ready to read/write */ + v24 = TIOCM_DTR | TIOCM_RTS; + ioctl(fd, TIOCMBIS, &v24); + + return fd; +} + +/* Read the to-be-downloaded file, prepend header and length, append XOR sum */ +int read_file(const char *filename) +{ + int fd, rc, i; + struct stat st; + const uint8_t *hdr; + int hdr_len = 0; + uint8_t *file_data; + uint16_t tot_len; + uint8_t nibble; + uint8_t running_xor = 0x02; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("opening file"); + exit(1); + } + + rc = fstat(fd, &st); + if (st.st_size > MAX_DNLOAD_SIZE) { + fprintf(stderr, "The maximum file size is 64kBytes (%u bytes)\n", + MAX_DNLOAD_SIZE); + return -EFBIG; + } + + if (dnload.data) { + free(dnload.data); + dnload.data = NULL; + } + + dnload.data = malloc(MAX_HDR_SIZE + st.st_size); + if (!dnload.data) { + close(fd); + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + + /* copy in the header, if any */ + switch (dnload.mode) { + case MODE_C155: + hdr = data_hdr_c155; + hdr_len = sizeof(data_hdr_c155); + break; + case MODE_C123: + case MODE_C123xor: + hdr = data_hdr_c123; + hdr_len = sizeof(data_hdr_c123); + break; + default: + break; + } + + if (hdr && hdr_len) + memcpy(dnload.data, hdr, hdr_len); + + /* 2 bytes for length + header */ + file_data = dnload.data + 2 + hdr_len; + + /* write the length, keep running XOR */ + tot_len = hdr_len + st.st_size; + nibble = tot_len >> 8; + dnload.data[0] = nibble; + running_xor ^= nibble; + nibble = tot_len & 0xff; + dnload.data[1] = nibble; + running_xor ^= nibble; + + if (hdr_len && hdr) { + memcpy(dnload.data+2, hdr, hdr_len); + + for (i = 0; i < hdr_len; i++) + running_xor ^= hdr[i]; + } + + rc = read(fd, file_data, st.st_size); + if (rc < 0) { + perror("error reading file\n"); + free(dnload.data); + dnload.data = NULL; + close(fd); + return -EIO; + } + if (rc < st.st_size) { + free(dnload.data); + dnload.data = NULL; + close(fd); + fprintf(stderr, "Short read of file (%d < %d)\n", + rc, (int)st.st_size); + return -EIO; + } + + close(fd); + + dnload.data_len = (file_data+st.st_size) - dnload.data; + + /* calculate XOR sum */ + for (i = 0; i < st.st_size; i++) + running_xor ^= file_data[i]; + + dnload.data[dnload.data_len++] = running_xor; + + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + + printf("read_file(%s): file_size=%u, hdr_len=%u, dnload_len=%u\n", + filename, (int)st.st_size, hdr_len, dnload.data_len); + + return 0; +} + +static void hexdump(const uint8_t *data, unsigned int len) +{ + const uint8_t *bufptr = data; + int n; + + for (n=0; bufptr, n < len; n++, bufptr++) + printf("%02x ", *bufptr); + printf("\n"); +} + +#define WRITE_BLOCK 4096 + +static int handle_write(void) +{ + int bytes_left, write_len, rc; + + printf("handle_write(): "); + if (dnload.write_ptr == dnload.data) { + /* no bytes have been transferred yet */ + if (dnload.mode == MODE_C155 || + dnload.mode == MODE_C123xor) { + uint8_t xor_init = 0x02; + write(dnload.serial_fd.fd, &xor_init, 1); + } else + usleep(1); + } else if (dnload.write_ptr >= dnload.data + dnload.data_len) { + printf("finished\n"); + dnload.write_ptr = dnload.data; + return 1; + } + + /* try to write a maximum of WRITE_BLOCK bytes */ + bytes_left = (dnload.data + dnload.data_len) - dnload.write_ptr; + write_len = WRITE_BLOCK; + if (bytes_left < WRITE_BLOCK) + write_len = bytes_left; + + rc = write(dnload.serial_fd.fd, dnload.write_ptr, write_len); + if (rc < 0) { + perror("Error during write"); + return rc; + } + + dnload.write_ptr += rc; + + printf("%u bytes (%tu/%u)\n", rc, dnload.write_ptr - dnload.data, dnload.data_len); + + return 0; +} + +static uint8_t buffer[sizeof(phone_prompt1)]; +static uint8_t *bufptr = buffer; + +static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len) +{ + struct msgb *msg; + uint8_t c, *dest; + + if (len > 512) { + fprintf(stderr, "Too much data to send. %u\n", len); + return; + } + + /* push the message into the stack */ + msg = sercomm_alloc_msgb(512); + if (!msg) { + fprintf(stderr, "Failed to create data for the frame.\n"); + return; + } + + /* copy the data */ + dest = msgb_put(msg, len); + memcpy(dest, data, len); + + sercomm_sendmsg(dlci, msg); + + /* drain the queue: TODO: do this through the select */ + while (sercomm_drv_pull(&c) != 0) + if (write(dnload.serial_fd.fd, &c, 1) != 1) + perror("short write"); +} + +static void hdlc_console_cb(uint8_t dlci, struct msgb *msg) +{ + write(1, msg->data, msg->len); + msgb_free(msg); +} + +static void hdlc_l1a_cb(uint8_t dlci, struct msgb *msg) +{ + struct layer2_connection *con; + u_int16_t *len; + + len = (u_int16_t *) msgb_push(msg, 2); + *len = htons(msg->len - sizeof(*len)); + + llist_for_each_entry(con, &connections, entry) { + if (write(con->fd.fd, msg->data, msg->len) != msg->len) { + fprintf(stderr, "Failed to write msg to the socket..\n"); + continue; + } + } + + msgb_free(msg); +} + +static void print_hdlc(uint8_t *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + if (sercomm_drv_rx_char(buffer[i]) == 0) + printf("Dropping sample '%c'\n", buffer[i]); +} + +static int handle_read(void) +{ + int rc, nbytes, buf_left; + + buf_left = sizeof(buffer) - (bufptr - buffer); + if (buf_left <= 0) { + memmove(buffer, buffer+1, sizeof(buffer)-1); + bufptr -= 1; + buf_left = 1; + } + + nbytes = read(dnload.serial_fd.fd, bufptr, buf_left); + if (nbytes <= 0) + return nbytes; + + if (!dnload.print_hdlc) { + printf("got %i bytes from modem, ", nbytes); + printf("data looks like: "); + hexdump(bufptr, nbytes); + } else { + print_hdlc(bufptr, nbytes); + } + + if (!memcmp(buffer, phone_prompt1, sizeof(phone_prompt1))) { + printf("Received PROMPT1 from phone, responding with CMD\n"); + dnload.print_hdlc = 0; + dnload.state = WAITING_PROMPT2; + rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd)); + + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", dnload.filename, rc); + exit(1); + } + } else if (!memcmp(buffer, phone_prompt2, sizeof(phone_prompt2))) { + printf("Received PROMPT2 from phone, starting download\n"); + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + dnload.state = DOWNLOADING; + } else if (!memcmp(buffer, phone_ack, sizeof(phone_ack))) { + printf("Received DOWNLOAD ACK from phone, your code is running now!\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + dnload.print_hdlc = 1; + } else if (!memcmp(buffer, phone_nack, sizeof(phone_nack))) { + printf("Received DOWNLOAD NACK from phone, something went wrong :(\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, phone_nack_magic, sizeof(phone_nack_magic))) { + printf("Received MAGIC NACK from phone, you need to have \"1003\" at 0x803ce0\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, ftmtool, sizeof(ftmtool))) { + printf("Received FTMTOOL from phone, ramolader has aborted\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } + bufptr += nbytes; + + return nbytes; +} + +static int serial_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc; + + if (flags & BSC_FD_READ) { + rc = handle_read(); + if (rc == 0) + exit(2); + } + + if (flags & BSC_FD_WRITE) { + rc = handle_write(); + if (rc == 1) + dnload.state = WAITING_PROMPT1; + } +} + +static int parse_mode(const char *arg) +{ + if (!strcasecmp(arg, "c123") || + !strcasecmp(arg, "c140")) + return MODE_C123; + else if (!strcasecmp(arg, "c123xor")) + return MODE_C123xor; + else if (!strcasecmp(arg, "c155")) + return MODE_C155; + + return -1; +} + + +static int usage(const char *name) +{ + printf("\nUsage: %s [ -v | -h ] [ -p /dev/ttyXXXX ] [ -s /tmp/osmocom_l2 ] ][ -m {c123,c123xor,c155} ] file.bin\n", name); + printf("\t* Open serial port /dev/ttyXXXX (connected to your phone)\n" + "\t* Perform handshaking with the ramloader in the phone\n" + "\t* Download file.bin to the attached phone (base address 0x00800100)\n"); + exit(2); +} + +static int version(const char *name) +{ + printf("\n%s version %s\n", name, VERSION); + exit(2); +} + +static int un_layer2_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc; + u_int16_t length = 0xffff; + char buf[4096]; + struct layer2_connection *con; + + + rc = read(fd->fd, &length, sizeof(length)); + if (rc <= 0 || ntohs(length) > 512) { + fprintf(stderr, "Unexpected result from socket. rc: %d len: %d\n", + rc, ntohs(length)); + goto close; + } + + rc = read(fd->fd, buf, ntohs(length)); + if (rc != ntohs(length)) { + fprintf(stderr, "Could not read data.\n"); + goto close; + } + + hdlc_send_to_phone(SC_DLCI_L1A_L23, buf, ntohs(length)); + + return 0; +close: + con = (struct layer2_connection *) fd->data; + + close(fd->fd); + bsc_unregister_fd(fd); + llist_del(&con->entry); + talloc_free(con); + return -1; +} + +/* accept a new connection */ +static int un_layer2_accept(struct bsc_fd *fd, unsigned int flags) +{ + struct layer2_connection *con; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(fd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 0) { + fprintf(stderr, "Failed to accept a new connection.\n"); + return -1; + } + + con = talloc_zero(NULL, struct layer2_connection); + if (!con) { + fprintf(stderr, "Failed to create layer2 connection.\n"); + return -1; + } + + con->fd.fd = rc; + con->fd.when = BSC_FD_READ; + con->fd.cb = un_layer2_read; + con->fd.data = con; + if (bsc_register_fd(&con->fd) != 0) { + fprintf(stderr, "Failed to register the fd.\n"); + return -1; + } + + llist_add(&con->entry, &connections); +} + +/* + * Create a server socket for the layer2 stack + */ +static int register_af_unix(const char *un_path) +{ + struct sockaddr_un local; + int rc; + + dnload.socket.fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (dnload.socket.fd < 0) { + fprintf(stderr, "Failed to create Unix Domain Socket.\n"); + return -1; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, un_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + rc = bind(dnload.socket.fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc != 0) { + fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", + local.sun_path); + return -1; + } + + if (listen(dnload.socket.fd, 0) != 0) { + fprintf(stderr, "Failed to listen.\n"); + return -1; + } + + dnload.socket.when = BSC_FD_READ; + dnload.socket.cb = un_layer2_accept; + + if (bsc_register_fd(&dnload.socket) != 0) { + fprintf(stderr, "Failed to register the bfd.\n"); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int opt, flags; + char *serial_dev = "/dev/ttyUSB1"; + char *un_path = "/tmp/osmocom_l2"; + + dnload.mode = MODE_C123; + + while ((opt = getopt(argc, argv, "hp:m:s:v")) != -1) { + switch (opt) { + case 'p': + serial_dev = optarg; + break; + case 'm': + dnload.mode = parse_mode(optarg); + if (dnload.mode < 0) + usage(argv[0]); + break; + case 's': + un_path = optarg; + break; + case 'v': + version(argv[0]); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + if (argc <= optind) { + fprintf(stderr, "You have to specify the filename\n"); + usage(argv[0]); + } + + dnload.filename = argv[optind]; + + dnload.serial_fd.fd = serial_init(serial_dev); + if (dnload.serial_fd.fd < 0) { + fprintf(stderr, "Cannot open serial device %s\n", serial_dev); + exit(1); + } + + if (bsc_register_fd(&dnload.serial_fd) != 0) { + fprintf(stderr, "Failed to register the serial.\n"); + exit(1); + } + + /* Set serial socket to non-blocking mode of operation */ + flags = fcntl(dnload.serial_fd.fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(dnload.serial_fd.fd, F_SETFL, flags); + + dnload.serial_fd.when = BSC_FD_READ; + dnload.serial_fd.cb = serial_read; + + /* unix domain socket handling */ + if (register_af_unix(un_path) != 0) + exit(1); + + + /* initialize the HDLC layer */ + sercomm_init(); + sercomm_register_rx_cb(SC_DLCI_CONSOLE, hdlc_console_cb); + sercomm_register_rx_cb(SC_DLCI_L1A_L23, hdlc_l1a_cb); + while (1) + bsc_select_main(0); + + close(dnload.serial_fd.fd); + + exit(0); +} diff --git a/src/host/rita_pll/rita_pll.pl b/src/host/rita_pll/rita_pll.pl new file mode 100755 index 000000000..7de1aec44 --- /dev/null +++ b/src/host/rita_pll/rita_pll.pl @@ -0,0 +1,111 @@ +#!/usr/bin/perl + +sub pll_rx($$$$$) { + my ($a, $b, $p, $r, $l) = @_; + + return (($b*$p+$a)/($r*$l))*26; +} + +sub pll_rx_low_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 4; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_rx_high_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 2; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_tx_gsm850_1($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 55; my $l = 4; my $m = 26; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm850_2($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 30; my $l = 4; my $m = 52; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm900($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 35; my $l = 4; my $m = 52; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_high($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 70; my $l = 2; my $m = 26; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub hr() { + printf("======================================================================\n"); +} + +printf("PLL Rx Low Band:\n"); +for (my $b = 135; $b <= 150; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_low_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Rx High Band:\n"); +for (my $b = 141; $b <= 155; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_high_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_1\n"); +for (my $b = 128; $b <= 130; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_1($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_2\n"); +for (my $b = 65; $b <= 66; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_2($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM900\n"); +for (my $b = 68; $b <= 71; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm900($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM1800/1900\n"); +for (my $b = 133; $b <= 149; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_high($a, $b), $a, $b); + } +} + diff --git a/src/host/rita_pll/rita_pll.txt b/src/host/rita_pll/rita_pll.txt new file mode 100644 index 000000000..cac2cac2e --- /dev/null +++ b/src/host/rita_pll/rita_pll.txt @@ -0,0 +1,3625 @@ +PLL Rx Low Band: +Fout=864.00 (A=000, B=135) +Fout=864.10 (A=001, B=135) +Fout=864.20 (A=002, B=135) +Fout=864.30 (A=003, B=135) +Fout=864.40 (A=004, B=135) +Fout=864.50 (A=005, B=135) +Fout=864.60 (A=006, B=135) +Fout=864.70 (A=007, B=135) +Fout=864.80 (A=008, B=135) +Fout=864.90 (A=009, B=135) +Fout=865.00 (A=010, B=135) +Fout=865.10 (A=011, B=135) +Fout=865.20 (A=012, B=135) +Fout=865.30 (A=013, B=135) +Fout=865.40 (A=014, B=135) +Fout=865.50 (A=015, B=135) +Fout=865.60 (A=016, B=135) +Fout=865.70 (A=017, B=135) +Fout=865.80 (A=018, B=135) +Fout=865.90 (A=019, B=135) +Fout=866.00 (A=020, B=135) +Fout=866.10 (A=021, B=135) +Fout=866.20 (A=022, B=135) +Fout=866.30 (A=023, B=135) +Fout=866.40 (A=024, B=135) +Fout=866.50 (A=025, B=135) +Fout=866.60 (A=026, B=135) +Fout=866.70 (A=027, B=135) +Fout=866.80 (A=028, B=135) +Fout=866.90 (A=029, B=135) +Fout=867.00 (A=030, B=135) +Fout=867.10 (A=031, B=135) +Fout=867.20 (A=032, B=135) +Fout=867.30 (A=033, B=135) +Fout=867.40 (A=034, B=135) +Fout=867.50 (A=035, B=135) +Fout=867.60 (A=036, B=135) +Fout=867.70 (A=037, B=135) +Fout=867.80 (A=038, B=135) +Fout=867.90 (A=039, B=135) +Fout=868.00 (A=040, B=135) +Fout=868.10 (A=041, B=135) +Fout=868.20 (A=042, B=135) +Fout=868.30 (A=043, B=135) +Fout=868.40 (A=044, B=135) +Fout=868.50 (A=045, B=135) +Fout=868.60 (A=046, B=135) +Fout=868.70 (A=047, B=135) +Fout=868.80 (A=048, B=135) +Fout=868.90 (A=049, B=135) +Fout=869.00 (A=050, B=135) +Fout=869.10 (A=051, B=135) +Fout=869.20 (A=052, B=135) +Fout=869.30 (A=053, B=135) +Fout=869.40 (A=054, B=135) +Fout=869.50 (A=055, B=135) +Fout=869.60 (A=056, B=135) +Fout=869.70 (A=057, B=135) +Fout=869.80 (A=058, B=135) +Fout=869.90 (A=059, B=135) +Fout=870.00 (A=060, B=135) +Fout=870.10 (A=061, B=135) +Fout=870.20 (A=062, B=135) +Fout=870.40 (A=000, B=136) +Fout=870.50 (A=001, B=136) +Fout=870.60 (A=002, B=136) +Fout=870.70 (A=003, B=136) +Fout=870.80 (A=004, B=136) +Fout=870.90 (A=005, B=136) +Fout=871.00 (A=006, B=136) +Fout=871.10 (A=007, B=136) +Fout=871.20 (A=008, B=136) +Fout=871.30 (A=009, B=136) +Fout=871.40 (A=010, B=136) +Fout=871.50 (A=011, B=136) +Fout=871.60 (A=012, B=136) +Fout=871.70 (A=013, B=136) +Fout=871.80 (A=014, B=136) +Fout=871.90 (A=015, B=136) +Fout=872.00 (A=016, B=136) +Fout=872.10 (A=017, B=136) +Fout=872.20 (A=018, B=136) +Fout=872.30 (A=019, B=136) +Fout=872.40 (A=020, B=136) +Fout=872.50 (A=021, B=136) +Fout=872.60 (A=022, B=136) +Fout=872.70 (A=023, B=136) +Fout=872.80 (A=024, B=136) +Fout=872.90 (A=025, B=136) +Fout=873.00 (A=026, B=136) +Fout=873.10 (A=027, B=136) +Fout=873.20 (A=028, B=136) +Fout=873.30 (A=029, B=136) +Fout=873.40 (A=030, B=136) +Fout=873.50 (A=031, B=136) +Fout=873.60 (A=032, B=136) +Fout=873.70 (A=033, B=136) +Fout=873.80 (A=034, B=136) +Fout=873.90 (A=035, B=136) +Fout=874.00 (A=036, B=136) +Fout=874.10 (A=037, B=136) +Fout=874.20 (A=038, B=136) +Fout=874.30 (A=039, B=136) +Fout=874.40 (A=040, B=136) +Fout=874.50 (A=041, B=136) +Fout=874.60 (A=042, B=136) +Fout=874.70 (A=043, B=136) +Fout=874.80 (A=044, B=136) +Fout=874.90 (A=045, B=136) +Fout=875.00 (A=046, B=136) +Fout=875.10 (A=047, B=136) +Fout=875.20 (A=048, B=136) +Fout=875.30 (A=049, B=136) +Fout=875.40 (A=050, B=136) +Fout=875.50 (A=051, B=136) +Fout=875.60 (A=052, B=136) +Fout=875.70 (A=053, B=136) +Fout=875.80 (A=054, B=136) +Fout=875.90 (A=055, B=136) +Fout=876.00 (A=056, B=136) +Fout=876.10 (A=057, B=136) +Fout=876.20 (A=058, B=136) +Fout=876.30 (A=059, B=136) +Fout=876.40 (A=060, B=136) +Fout=876.50 (A=061, B=136) +Fout=876.60 (A=062, B=136) +Fout=876.80 (A=000, B=137) +Fout=876.90 (A=001, B=137) +Fout=877.00 (A=002, B=137) +Fout=877.10 (A=003, B=137) +Fout=877.20 (A=004, B=137) +Fout=877.30 (A=005, B=137) +Fout=877.40 (A=006, B=137) +Fout=877.50 (A=007, B=137) +Fout=877.60 (A=008, B=137) +Fout=877.70 (A=009, B=137) +Fout=877.80 (A=010, B=137) +Fout=877.90 (A=011, B=137) +Fout=878.00 (A=012, B=137) +Fout=878.10 (A=013, B=137) +Fout=878.20 (A=014, B=137) +Fout=878.30 (A=015, B=137) +Fout=878.40 (A=016, B=137) +Fout=878.50 (A=017, B=137) +Fout=878.60 (A=018, B=137) +Fout=878.70 (A=019, B=137) +Fout=878.80 (A=020, B=137) +Fout=878.90 (A=021, B=137) +Fout=879.00 (A=022, B=137) +Fout=879.10 (A=023, B=137) +Fout=879.20 (A=024, B=137) +Fout=879.30 (A=025, B=137) +Fout=879.40 (A=026, B=137) +Fout=879.50 (A=027, B=137) +Fout=879.60 (A=028, B=137) +Fout=879.70 (A=029, B=137) +Fout=879.80 (A=030, B=137) +Fout=879.90 (A=031, B=137) +Fout=880.00 (A=032, B=137) +Fout=880.10 (A=033, B=137) +Fout=880.20 (A=034, B=137) +Fout=880.30 (A=035, B=137) +Fout=880.40 (A=036, B=137) +Fout=880.50 (A=037, B=137) +Fout=880.60 (A=038, B=137) +Fout=880.70 (A=039, B=137) +Fout=880.80 (A=040, B=137) +Fout=880.90 (A=041, B=137) +Fout=881.00 (A=042, B=137) +Fout=881.10 (A=043, B=137) +Fout=881.20 (A=044, B=137) +Fout=881.30 (A=045, B=137) +Fout=881.40 (A=046, B=137) +Fout=881.50 (A=047, B=137) +Fout=881.60 (A=048, B=137) +Fout=881.70 (A=049, B=137) +Fout=881.80 (A=050, B=137) +Fout=881.90 (A=051, B=137) +Fout=882.00 (A=052, B=137) +Fout=882.10 (A=053, B=137) +Fout=882.20 (A=054, B=137) +Fout=882.30 (A=055, B=137) +Fout=882.40 (A=056, B=137) +Fout=882.50 (A=057, B=137) +Fout=882.60 (A=058, B=137) +Fout=882.70 (A=059, B=137) +Fout=882.80 (A=060, B=137) +Fout=882.90 (A=061, B=137) +Fout=883.00 (A=062, B=137) +Fout=883.20 (A=000, B=138) +Fout=883.30 (A=001, B=138) +Fout=883.40 (A=002, B=138) +Fout=883.50 (A=003, B=138) +Fout=883.60 (A=004, B=138) +Fout=883.70 (A=005, B=138) +Fout=883.80 (A=006, B=138) +Fout=883.90 (A=007, B=138) +Fout=884.00 (A=008, B=138) +Fout=884.10 (A=009, B=138) +Fout=884.20 (A=010, B=138) +Fout=884.30 (A=011, B=138) +Fout=884.40 (A=012, B=138) +Fout=884.50 (A=013, B=138) +Fout=884.60 (A=014, B=138) +Fout=884.70 (A=015, B=138) +Fout=884.80 (A=016, B=138) +Fout=884.90 (A=017, B=138) +Fout=885.00 (A=018, B=138) +Fout=885.10 (A=019, B=138) +Fout=885.20 (A=020, B=138) +Fout=885.30 (A=021, B=138) +Fout=885.40 (A=022, B=138) +Fout=885.50 (A=023, B=138) +Fout=885.60 (A=024, B=138) +Fout=885.70 (A=025, B=138) +Fout=885.80 (A=026, B=138) +Fout=885.90 (A=027, B=138) +Fout=886.00 (A=028, B=138) +Fout=886.10 (A=029, B=138) +Fout=886.20 (A=030, B=138) +Fout=886.30 (A=031, B=138) +Fout=886.40 (A=032, B=138) +Fout=886.50 (A=033, B=138) +Fout=886.60 (A=034, B=138) +Fout=886.70 (A=035, B=138) +Fout=886.80 (A=036, B=138) +Fout=886.90 (A=037, B=138) +Fout=887.00 (A=038, B=138) +Fout=887.10 (A=039, B=138) +Fout=887.20 (A=040, B=138) +Fout=887.30 (A=041, B=138) +Fout=887.40 (A=042, B=138) +Fout=887.50 (A=043, B=138) +Fout=887.60 (A=044, B=138) +Fout=887.70 (A=045, B=138) +Fout=887.80 (A=046, B=138) +Fout=887.90 (A=047, B=138) +Fout=888.00 (A=048, B=138) +Fout=888.10 (A=049, B=138) +Fout=888.20 (A=050, B=138) +Fout=888.30 (A=051, B=138) +Fout=888.40 (A=052, B=138) +Fout=888.50 (A=053, B=138) +Fout=888.60 (A=054, B=138) +Fout=888.70 (A=055, B=138) +Fout=888.80 (A=056, B=138) +Fout=888.90 (A=057, B=138) +Fout=889.00 (A=058, B=138) +Fout=889.10 (A=059, B=138) +Fout=889.20 (A=060, B=138) +Fout=889.30 (A=061, B=138) +Fout=889.40 (A=062, B=138) +Fout=889.60 (A=000, B=139) +Fout=889.70 (A=001, B=139) +Fout=889.80 (A=002, B=139) +Fout=889.90 (A=003, B=139) +Fout=890.00 (A=004, B=139) +Fout=890.10 (A=005, B=139) +Fout=890.20 (A=006, B=139) +Fout=890.30 (A=007, B=139) +Fout=890.40 (A=008, B=139) +Fout=890.50 (A=009, B=139) +Fout=890.60 (A=010, B=139) +Fout=890.70 (A=011, B=139) +Fout=890.80 (A=012, B=139) +Fout=890.90 (A=013, B=139) +Fout=891.00 (A=014, B=139) +Fout=891.10 (A=015, B=139) +Fout=891.20 (A=016, B=139) +Fout=891.30 (A=017, B=139) +Fout=891.40 (A=018, B=139) +Fout=891.50 (A=019, B=139) +Fout=891.60 (A=020, B=139) +Fout=891.70 (A=021, B=139) +Fout=891.80 (A=022, B=139) +Fout=891.90 (A=023, B=139) +Fout=892.00 (A=024, B=139) +Fout=892.10 (A=025, B=139) +Fout=892.20 (A=026, B=139) +Fout=892.30 (A=027, B=139) +Fout=892.40 (A=028, B=139) +Fout=892.50 (A=029, B=139) +Fout=892.60 (A=030, B=139) +Fout=892.70 (A=031, B=139) +Fout=892.80 (A=032, B=139) +Fout=892.90 (A=033, B=139) +Fout=893.00 (A=034, B=139) +Fout=893.10 (A=035, B=139) +Fout=893.20 (A=036, B=139) +Fout=893.30 (A=037, B=139) +Fout=893.40 (A=038, B=139) +Fout=893.50 (A=039, B=139) +Fout=893.60 (A=040, B=139) +Fout=893.70 (A=041, B=139) +Fout=893.80 (A=042, B=139) +Fout=893.90 (A=043, B=139) +Fout=894.00 (A=044, B=139) +Fout=894.10 (A=045, B=139) +Fout=894.20 (A=046, B=139) +Fout=894.30 (A=047, B=139) +Fout=894.40 (A=048, B=139) +Fout=894.50 (A=049, B=139) +Fout=894.60 (A=050, B=139) +Fout=894.70 (A=051, B=139) +Fout=894.80 (A=052, B=139) +Fout=894.90 (A=053, B=139) +Fout=895.00 (A=054, B=139) +Fout=895.10 (A=055, B=139) +Fout=895.20 (A=056, B=139) +Fout=895.30 (A=057, B=139) +Fout=895.40 (A=058, B=139) +Fout=895.50 (A=059, B=139) +Fout=895.60 (A=060, B=139) +Fout=895.70 (A=061, B=139) +Fout=895.80 (A=062, B=139) +Fout=896.00 (A=000, B=140) +Fout=896.10 (A=001, B=140) +Fout=896.20 (A=002, B=140) +Fout=896.30 (A=003, B=140) +Fout=896.40 (A=004, B=140) +Fout=896.50 (A=005, B=140) +Fout=896.60 (A=006, B=140) +Fout=896.70 (A=007, B=140) +Fout=896.80 (A=008, B=140) +Fout=896.90 (A=009, B=140) +Fout=897.00 (A=010, B=140) +Fout=897.10 (A=011, B=140) +Fout=897.20 (A=012, B=140) +Fout=897.30 (A=013, B=140) +Fout=897.40 (A=014, B=140) +Fout=897.50 (A=015, B=140) +Fout=897.60 (A=016, B=140) +Fout=897.70 (A=017, B=140) +Fout=897.80 (A=018, B=140) +Fout=897.90 (A=019, B=140) +Fout=898.00 (A=020, B=140) +Fout=898.10 (A=021, B=140) +Fout=898.20 (A=022, B=140) +Fout=898.30 (A=023, B=140) +Fout=898.40 (A=024, B=140) +Fout=898.50 (A=025, B=140) +Fout=898.60 (A=026, B=140) +Fout=898.70 (A=027, B=140) +Fout=898.80 (A=028, B=140) +Fout=898.90 (A=029, B=140) +Fout=899.00 (A=030, B=140) +Fout=899.10 (A=031, B=140) +Fout=899.20 (A=032, B=140) +Fout=899.30 (A=033, B=140) +Fout=899.40 (A=034, B=140) +Fout=899.50 (A=035, B=140) +Fout=899.60 (A=036, B=140) +Fout=899.70 (A=037, B=140) +Fout=899.80 (A=038, B=140) +Fout=899.90 (A=039, B=140) +Fout=900.00 (A=040, B=140) +Fout=900.10 (A=041, B=140) +Fout=900.20 (A=042, B=140) +Fout=900.30 (A=043, B=140) +Fout=900.40 (A=044, B=140) +Fout=900.50 (A=045, B=140) +Fout=900.60 (A=046, B=140) +Fout=900.70 (A=047, B=140) +Fout=900.80 (A=048, B=140) +Fout=900.90 (A=049, B=140) +Fout=901.00 (A=050, B=140) +Fout=901.10 (A=051, B=140) +Fout=901.20 (A=052, B=140) +Fout=901.30 (A=053, B=140) +Fout=901.40 (A=054, B=140) +Fout=901.50 (A=055, B=140) +Fout=901.60 (A=056, B=140) +Fout=901.70 (A=057, B=140) +Fout=901.80 (A=058, B=140) +Fout=901.90 (A=059, B=140) +Fout=902.00 (A=060, B=140) +Fout=902.10 (A=061, B=140) +Fout=902.20 (A=062, B=140) +Fout=902.40 (A=000, B=141) +Fout=902.50 (A=001, B=141) +Fout=902.60 (A=002, B=141) +Fout=902.70 (A=003, B=141) +Fout=902.80 (A=004, B=141) +Fout=902.90 (A=005, B=141) +Fout=903.00 (A=006, B=141) +Fout=903.10 (A=007, B=141) +Fout=903.20 (A=008, B=141) +Fout=903.30 (A=009, B=141) +Fout=903.40 (A=010, B=141) +Fout=903.50 (A=011, B=141) +Fout=903.60 (A=012, B=141) +Fout=903.70 (A=013, B=141) +Fout=903.80 (A=014, B=141) +Fout=903.90 (A=015, B=141) +Fout=904.00 (A=016, B=141) +Fout=904.10 (A=017, B=141) +Fout=904.20 (A=018, B=141) +Fout=904.30 (A=019, B=141) +Fout=904.40 (A=020, B=141) +Fout=904.50 (A=021, B=141) +Fout=904.60 (A=022, B=141) +Fout=904.70 (A=023, B=141) +Fout=904.80 (A=024, B=141) +Fout=904.90 (A=025, B=141) +Fout=905.00 (A=026, B=141) +Fout=905.10 (A=027, B=141) +Fout=905.20 (A=028, B=141) +Fout=905.30 (A=029, B=141) +Fout=905.40 (A=030, B=141) +Fout=905.50 (A=031, B=141) +Fout=905.60 (A=032, B=141) +Fout=905.70 (A=033, B=141) +Fout=905.80 (A=034, B=141) +Fout=905.90 (A=035, B=141) +Fout=906.00 (A=036, B=141) +Fout=906.10 (A=037, B=141) +Fout=906.20 (A=038, B=141) +Fout=906.30 (A=039, B=141) +Fout=906.40 (A=040, B=141) +Fout=906.50 (A=041, B=141) +Fout=906.60 (A=042, B=141) +Fout=906.70 (A=043, B=141) +Fout=906.80 (A=044, B=141) +Fout=906.90 (A=045, B=141) +Fout=907.00 (A=046, B=141) +Fout=907.10 (A=047, B=141) +Fout=907.20 (A=048, B=141) +Fout=907.30 (A=049, B=141) +Fout=907.40 (A=050, B=141) +Fout=907.50 (A=051, B=141) +Fout=907.60 (A=052, B=141) +Fout=907.70 (A=053, B=141) +Fout=907.80 (A=054, B=141) +Fout=907.90 (A=055, B=141) +Fout=908.00 (A=056, B=141) +Fout=908.10 (A=057, B=141) +Fout=908.20 (A=058, B=141) +Fout=908.30 (A=059, B=141) +Fout=908.40 (A=060, B=141) +Fout=908.50 (A=061, B=141) +Fout=908.60 (A=062, B=141) +Fout=908.80 (A=000, B=142) +Fout=908.90 (A=001, B=142) +Fout=909.00 (A=002, B=142) +Fout=909.10 (A=003, B=142) +Fout=909.20 (A=004, B=142) +Fout=909.30 (A=005, B=142) +Fout=909.40 (A=006, B=142) +Fout=909.50 (A=007, B=142) +Fout=909.60 (A=008, B=142) +Fout=909.70 (A=009, B=142) +Fout=909.80 (A=010, B=142) +Fout=909.90 (A=011, B=142) +Fout=910.00 (A=012, B=142) +Fout=910.10 (A=013, B=142) +Fout=910.20 (A=014, B=142) +Fout=910.30 (A=015, B=142) +Fout=910.40 (A=016, B=142) +Fout=910.50 (A=017, B=142) +Fout=910.60 (A=018, B=142) +Fout=910.70 (A=019, B=142) +Fout=910.80 (A=020, B=142) +Fout=910.90 (A=021, B=142) +Fout=911.00 (A=022, B=142) +Fout=911.10 (A=023, B=142) +Fout=911.20 (A=024, B=142) +Fout=911.30 (A=025, B=142) +Fout=911.40 (A=026, B=142) +Fout=911.50 (A=027, B=142) +Fout=911.60 (A=028, B=142) +Fout=911.70 (A=029, B=142) +Fout=911.80 (A=030, B=142) +Fout=911.90 (A=031, B=142) +Fout=912.00 (A=032, B=142) +Fout=912.10 (A=033, B=142) +Fout=912.20 (A=034, B=142) +Fout=912.30 (A=035, B=142) +Fout=912.40 (A=036, B=142) +Fout=912.50 (A=037, B=142) +Fout=912.60 (A=038, B=142) +Fout=912.70 (A=039, B=142) +Fout=912.80 (A=040, B=142) +Fout=912.90 (A=041, B=142) +Fout=913.00 (A=042, B=142) +Fout=913.10 (A=043, B=142) +Fout=913.20 (A=044, B=142) +Fout=913.30 (A=045, B=142) +Fout=913.40 (A=046, B=142) +Fout=913.50 (A=047, B=142) +Fout=913.60 (A=048, B=142) +Fout=913.70 (A=049, B=142) +Fout=913.80 (A=050, B=142) +Fout=913.90 (A=051, B=142) +Fout=914.00 (A=052, B=142) +Fout=914.10 (A=053, B=142) +Fout=914.20 (A=054, B=142) +Fout=914.30 (A=055, B=142) +Fout=914.40 (A=056, B=142) +Fout=914.50 (A=057, B=142) +Fout=914.60 (A=058, B=142) +Fout=914.70 (A=059, B=142) +Fout=914.80 (A=060, B=142) +Fout=914.90 (A=061, B=142) +Fout=915.00 (A=062, B=142) +Fout=915.20 (A=000, B=143) +Fout=915.30 (A=001, B=143) +Fout=915.40 (A=002, B=143) +Fout=915.50 (A=003, B=143) +Fout=915.60 (A=004, B=143) +Fout=915.70 (A=005, B=143) +Fout=915.80 (A=006, B=143) +Fout=915.90 (A=007, B=143) +Fout=916.00 (A=008, B=143) +Fout=916.10 (A=009, B=143) +Fout=916.20 (A=010, B=143) +Fout=916.30 (A=011, B=143) +Fout=916.40 (A=012, B=143) +Fout=916.50 (A=013, B=143) +Fout=916.60 (A=014, B=143) +Fout=916.70 (A=015, B=143) +Fout=916.80 (A=016, B=143) +Fout=916.90 (A=017, B=143) +Fout=917.00 (A=018, B=143) +Fout=917.10 (A=019, B=143) +Fout=917.20 (A=020, B=143) +Fout=917.30 (A=021, B=143) +Fout=917.40 (A=022, B=143) +Fout=917.50 (A=023, B=143) +Fout=917.60 (A=024, B=143) +Fout=917.70 (A=025, B=143) +Fout=917.80 (A=026, B=143) +Fout=917.90 (A=027, B=143) +Fout=918.00 (A=028, B=143) +Fout=918.10 (A=029, B=143) +Fout=918.20 (A=030, B=143) +Fout=918.30 (A=031, B=143) +Fout=918.40 (A=032, B=143) +Fout=918.50 (A=033, B=143) +Fout=918.60 (A=034, B=143) +Fout=918.70 (A=035, B=143) +Fout=918.80 (A=036, B=143) +Fout=918.90 (A=037, B=143) +Fout=919.00 (A=038, B=143) +Fout=919.10 (A=039, B=143) +Fout=919.20 (A=040, B=143) +Fout=919.30 (A=041, B=143) +Fout=919.40 (A=042, B=143) +Fout=919.50 (A=043, B=143) +Fout=919.60 (A=044, B=143) +Fout=919.70 (A=045, B=143) +Fout=919.80 (A=046, B=143) +Fout=919.90 (A=047, B=143) +Fout=920.00 (A=048, B=143) +Fout=920.10 (A=049, B=143) +Fout=920.20 (A=050, B=143) +Fout=920.30 (A=051, B=143) +Fout=920.40 (A=052, B=143) +Fout=920.50 (A=053, B=143) +Fout=920.60 (A=054, B=143) +Fout=920.70 (A=055, B=143) +Fout=920.80 (A=056, B=143) +Fout=920.90 (A=057, B=143) +Fout=921.00 (A=058, B=143) +Fout=921.10 (A=059, B=143) +Fout=921.20 (A=060, B=143) +Fout=921.30 (A=061, B=143) +Fout=921.40 (A=062, B=143) +Fout=921.60 (A=000, B=144) +Fout=921.70 (A=001, B=144) +Fout=921.80 (A=002, B=144) +Fout=921.90 (A=003, B=144) +Fout=922.00 (A=004, B=144) +Fout=922.10 (A=005, B=144) +Fout=922.20 (A=006, B=144) +Fout=922.30 (A=007, B=144) +Fout=922.40 (A=008, B=144) +Fout=922.50 (A=009, B=144) +Fout=922.60 (A=010, B=144) +Fout=922.70 (A=011, B=144) +Fout=922.80 (A=012, B=144) +Fout=922.90 (A=013, B=144) +Fout=923.00 (A=014, B=144) +Fout=923.10 (A=015, B=144) +Fout=923.20 (A=016, B=144) +Fout=923.30 (A=017, B=144) +Fout=923.40 (A=018, B=144) +Fout=923.50 (A=019, B=144) +Fout=923.60 (A=020, B=144) +Fout=923.70 (A=021, B=144) +Fout=923.80 (A=022, B=144) +Fout=923.90 (A=023, B=144) +Fout=924.00 (A=024, B=144) +Fout=924.10 (A=025, B=144) +Fout=924.20 (A=026, B=144) +Fout=924.30 (A=027, B=144) +Fout=924.40 (A=028, B=144) +Fout=924.50 (A=029, B=144) +Fout=924.60 (A=030, B=144) +Fout=924.70 (A=031, B=144) +Fout=924.80 (A=032, B=144) +Fout=924.90 (A=033, B=144) +Fout=925.00 (A=034, B=144) +Fout=925.10 (A=035, B=144) +Fout=925.20 (A=036, B=144) +Fout=925.30 (A=037, B=144) +Fout=925.40 (A=038, B=144) +Fout=925.50 (A=039, B=144) +Fout=925.60 (A=040, B=144) +Fout=925.70 (A=041, B=144) +Fout=925.80 (A=042, B=144) +Fout=925.90 (A=043, B=144) +Fout=926.00 (A=044, B=144) +Fout=926.10 (A=045, B=144) +Fout=926.20 (A=046, B=144) +Fout=926.30 (A=047, B=144) +Fout=926.40 (A=048, B=144) +Fout=926.50 (A=049, B=144) +Fout=926.60 (A=050, B=144) +Fout=926.70 (A=051, B=144) +Fout=926.80 (A=052, B=144) +Fout=926.90 (A=053, B=144) +Fout=927.00 (A=054, B=144) +Fout=927.10 (A=055, B=144) +Fout=927.20 (A=056, B=144) +Fout=927.30 (A=057, B=144) +Fout=927.40 (A=058, B=144) +Fout=927.50 (A=059, B=144) +Fout=927.60 (A=060, B=144) +Fout=927.70 (A=061, B=144) +Fout=927.80 (A=062, B=144) +Fout=928.00 (A=000, B=145) +Fout=928.10 (A=001, B=145) +Fout=928.20 (A=002, B=145) +Fout=928.30 (A=003, B=145) +Fout=928.40 (A=004, B=145) +Fout=928.50 (A=005, B=145) +Fout=928.60 (A=006, B=145) +Fout=928.70 (A=007, B=145) +Fout=928.80 (A=008, B=145) +Fout=928.90 (A=009, B=145) +Fout=929.00 (A=010, B=145) +Fout=929.10 (A=011, B=145) +Fout=929.20 (A=012, B=145) +Fout=929.30 (A=013, B=145) +Fout=929.40 (A=014, B=145) +Fout=929.50 (A=015, B=145) +Fout=929.60 (A=016, B=145) +Fout=929.70 (A=017, B=145) +Fout=929.80 (A=018, B=145) +Fout=929.90 (A=019, B=145) +Fout=930.00 (A=020, B=145) +Fout=930.10 (A=021, B=145) +Fout=930.20 (A=022, B=145) +Fout=930.30 (A=023, B=145) +Fout=930.40 (A=024, B=145) +Fout=930.50 (A=025, B=145) +Fout=930.60 (A=026, B=145) +Fout=930.70 (A=027, B=145) +Fout=930.80 (A=028, B=145) +Fout=930.90 (A=029, B=145) +Fout=931.00 (A=030, B=145) +Fout=931.10 (A=031, B=145) +Fout=931.20 (A=032, B=145) +Fout=931.30 (A=033, B=145) +Fout=931.40 (A=034, B=145) +Fout=931.50 (A=035, B=145) +Fout=931.60 (A=036, B=145) +Fout=931.70 (A=037, B=145) +Fout=931.80 (A=038, B=145) +Fout=931.90 (A=039, B=145) +Fout=932.00 (A=040, B=145) +Fout=932.10 (A=041, B=145) +Fout=932.20 (A=042, B=145) +Fout=932.30 (A=043, B=145) +Fout=932.40 (A=044, B=145) +Fout=932.50 (A=045, B=145) +Fout=932.60 (A=046, B=145) +Fout=932.70 (A=047, B=145) +Fout=932.80 (A=048, B=145) +Fout=932.90 (A=049, B=145) +Fout=933.00 (A=050, B=145) +Fout=933.10 (A=051, B=145) +Fout=933.20 (A=052, B=145) +Fout=933.30 (A=053, B=145) +Fout=933.40 (A=054, B=145) +Fout=933.50 (A=055, B=145) +Fout=933.60 (A=056, B=145) +Fout=933.70 (A=057, B=145) +Fout=933.80 (A=058, B=145) +Fout=933.90 (A=059, B=145) +Fout=934.00 (A=060, B=145) +Fout=934.10 (A=061, B=145) +Fout=934.20 (A=062, B=145) +Fout=934.40 (A=000, B=146) +Fout=934.50 (A=001, B=146) +Fout=934.60 (A=002, B=146) +Fout=934.70 (A=003, B=146) +Fout=934.80 (A=004, B=146) +Fout=934.90 (A=005, B=146) +Fout=935.00 (A=006, B=146) +Fout=935.10 (A=007, B=146) +Fout=935.20 (A=008, B=146) +Fout=935.30 (A=009, B=146) +Fout=935.40 (A=010, B=146) +Fout=935.50 (A=011, B=146) +Fout=935.60 (A=012, B=146) +Fout=935.70 (A=013, B=146) +Fout=935.80 (A=014, B=146) +Fout=935.90 (A=015, B=146) +Fout=936.00 (A=016, B=146) +Fout=936.10 (A=017, B=146) +Fout=936.20 (A=018, B=146) +Fout=936.30 (A=019, B=146) +Fout=936.40 (A=020, B=146) +Fout=936.50 (A=021, B=146) +Fout=936.60 (A=022, B=146) +Fout=936.70 (A=023, B=146) +Fout=936.80 (A=024, B=146) +Fout=936.90 (A=025, B=146) +Fout=937.00 (A=026, B=146) +Fout=937.10 (A=027, B=146) +Fout=937.20 (A=028, B=146) +Fout=937.30 (A=029, B=146) +Fout=937.40 (A=030, B=146) +Fout=937.50 (A=031, B=146) +Fout=937.60 (A=032, B=146) +Fout=937.70 (A=033, B=146) +Fout=937.80 (A=034, B=146) +Fout=937.90 (A=035, B=146) +Fout=938.00 (A=036, B=146) +Fout=938.10 (A=037, B=146) +Fout=938.20 (A=038, B=146) +Fout=938.30 (A=039, B=146) +Fout=938.40 (A=040, B=146) +Fout=938.50 (A=041, B=146) +Fout=938.60 (A=042, B=146) +Fout=938.70 (A=043, B=146) +Fout=938.80 (A=044, B=146) +Fout=938.90 (A=045, B=146) +Fout=939.00 (A=046, B=146) +Fout=939.10 (A=047, B=146) +Fout=939.20 (A=048, B=146) +Fout=939.30 (A=049, B=146) +Fout=939.40 (A=050, B=146) +Fout=939.50 (A=051, B=146) +Fout=939.60 (A=052, B=146) +Fout=939.70 (A=053, B=146) +Fout=939.80 (A=054, B=146) +Fout=939.90 (A=055, B=146) +Fout=940.00 (A=056, B=146) +Fout=940.10 (A=057, B=146) +Fout=940.20 (A=058, B=146) +Fout=940.30 (A=059, B=146) +Fout=940.40 (A=060, B=146) +Fout=940.50 (A=061, B=146) +Fout=940.60 (A=062, B=146) +Fout=940.80 (A=000, B=147) +Fout=940.90 (A=001, B=147) +Fout=941.00 (A=002, B=147) +Fout=941.10 (A=003, B=147) +Fout=941.20 (A=004, B=147) +Fout=941.30 (A=005, B=147) +Fout=941.40 (A=006, B=147) +Fout=941.50 (A=007, B=147) +Fout=941.60 (A=008, B=147) +Fout=941.70 (A=009, B=147) +Fout=941.80 (A=010, B=147) +Fout=941.90 (A=011, B=147) +Fout=942.00 (A=012, B=147) +Fout=942.10 (A=013, B=147) +Fout=942.20 (A=014, B=147) +Fout=942.30 (A=015, B=147) +Fout=942.40 (A=016, B=147) +Fout=942.50 (A=017, B=147) +Fout=942.60 (A=018, B=147) +Fout=942.70 (A=019, B=147) +Fout=942.80 (A=020, B=147) +Fout=942.90 (A=021, B=147) +Fout=943.00 (A=022, B=147) +Fout=943.10 (A=023, B=147) +Fout=943.20 (A=024, B=147) +Fout=943.30 (A=025, B=147) +Fout=943.40 (A=026, B=147) +Fout=943.50 (A=027, B=147) +Fout=943.60 (A=028, B=147) +Fout=943.70 (A=029, B=147) +Fout=943.80 (A=030, B=147) +Fout=943.90 (A=031, B=147) +Fout=944.00 (A=032, B=147) +Fout=944.10 (A=033, B=147) +Fout=944.20 (A=034, B=147) +Fout=944.30 (A=035, B=147) +Fout=944.40 (A=036, B=147) +Fout=944.50 (A=037, B=147) +Fout=944.60 (A=038, B=147) +Fout=944.70 (A=039, B=147) +Fout=944.80 (A=040, B=147) +Fout=944.90 (A=041, B=147) +Fout=945.00 (A=042, B=147) +Fout=945.10 (A=043, B=147) +Fout=945.20 (A=044, B=147) +Fout=945.30 (A=045, B=147) +Fout=945.40 (A=046, B=147) +Fout=945.50 (A=047, B=147) +Fout=945.60 (A=048, B=147) +Fout=945.70 (A=049, B=147) +Fout=945.80 (A=050, B=147) +Fout=945.90 (A=051, B=147) +Fout=946.00 (A=052, B=147) +Fout=946.10 (A=053, B=147) +Fout=946.20 (A=054, B=147) +Fout=946.30 (A=055, B=147) +Fout=946.40 (A=056, B=147) +Fout=946.50 (A=057, B=147) +Fout=946.60 (A=058, B=147) +Fout=946.70 (A=059, B=147) +Fout=946.80 (A=060, B=147) +Fout=946.90 (A=061, B=147) +Fout=947.00 (A=062, B=147) +Fout=947.20 (A=000, B=148) +Fout=947.30 (A=001, B=148) +Fout=947.40 (A=002, B=148) +Fout=947.50 (A=003, B=148) +Fout=947.60 (A=004, B=148) +Fout=947.70 (A=005, B=148) +Fout=947.80 (A=006, B=148) +Fout=947.90 (A=007, B=148) +Fout=948.00 (A=008, B=148) +Fout=948.10 (A=009, B=148) +Fout=948.20 (A=010, B=148) +Fout=948.30 (A=011, B=148) +Fout=948.40 (A=012, B=148) +Fout=948.50 (A=013, B=148) +Fout=948.60 (A=014, B=148) +Fout=948.70 (A=015, B=148) +Fout=948.80 (A=016, B=148) +Fout=948.90 (A=017, B=148) +Fout=949.00 (A=018, B=148) +Fout=949.10 (A=019, B=148) +Fout=949.20 (A=020, B=148) +Fout=949.30 (A=021, B=148) +Fout=949.40 (A=022, B=148) +Fout=949.50 (A=023, B=148) +Fout=949.60 (A=024, B=148) +Fout=949.70 (A=025, B=148) +Fout=949.80 (A=026, B=148) +Fout=949.90 (A=027, B=148) +Fout=950.00 (A=028, B=148) +Fout=950.10 (A=029, B=148) +Fout=950.20 (A=030, B=148) +Fout=950.30 (A=031, B=148) +Fout=950.40 (A=032, B=148) +Fout=950.50 (A=033, B=148) +Fout=950.60 (A=034, B=148) +Fout=950.70 (A=035, B=148) +Fout=950.80 (A=036, B=148) +Fout=950.90 (A=037, B=148) +Fout=951.00 (A=038, B=148) +Fout=951.10 (A=039, B=148) +Fout=951.20 (A=040, B=148) +Fout=951.30 (A=041, B=148) +Fout=951.40 (A=042, B=148) +Fout=951.50 (A=043, B=148) +Fout=951.60 (A=044, B=148) +Fout=951.70 (A=045, B=148) +Fout=951.80 (A=046, B=148) +Fout=951.90 (A=047, B=148) +Fout=952.00 (A=048, B=148) +Fout=952.10 (A=049, B=148) +Fout=952.20 (A=050, B=148) +Fout=952.30 (A=051, B=148) +Fout=952.40 (A=052, B=148) +Fout=952.50 (A=053, B=148) +Fout=952.60 (A=054, B=148) +Fout=952.70 (A=055, B=148) +Fout=952.80 (A=056, B=148) +Fout=952.90 (A=057, B=148) +Fout=953.00 (A=058, B=148) +Fout=953.10 (A=059, B=148) +Fout=953.20 (A=060, B=148) +Fout=953.30 (A=061, B=148) +Fout=953.40 (A=062, B=148) +Fout=953.60 (A=000, B=149) +Fout=953.70 (A=001, B=149) +Fout=953.80 (A=002, B=149) +Fout=953.90 (A=003, B=149) +Fout=954.00 (A=004, B=149) +Fout=954.10 (A=005, B=149) +Fout=954.20 (A=006, B=149) +Fout=954.30 (A=007, B=149) +Fout=954.40 (A=008, B=149) +Fout=954.50 (A=009, B=149) +Fout=954.60 (A=010, B=149) +Fout=954.70 (A=011, B=149) +Fout=954.80 (A=012, B=149) +Fout=954.90 (A=013, B=149) +Fout=955.00 (A=014, B=149) +Fout=955.10 (A=015, B=149) +Fout=955.20 (A=016, B=149) +Fout=955.30 (A=017, B=149) +Fout=955.40 (A=018, B=149) +Fout=955.50 (A=019, B=149) +Fout=955.60 (A=020, B=149) +Fout=955.70 (A=021, B=149) +Fout=955.80 (A=022, B=149) +Fout=955.90 (A=023, B=149) +Fout=956.00 (A=024, B=149) +Fout=956.10 (A=025, B=149) +Fout=956.20 (A=026, B=149) +Fout=956.30 (A=027, B=149) +Fout=956.40 (A=028, B=149) +Fout=956.50 (A=029, B=149) +Fout=956.60 (A=030, B=149) +Fout=956.70 (A=031, B=149) +Fout=956.80 (A=032, B=149) +Fout=956.90 (A=033, B=149) +Fout=957.00 (A=034, B=149) +Fout=957.10 (A=035, B=149) +Fout=957.20 (A=036, B=149) +Fout=957.30 (A=037, B=149) +Fout=957.40 (A=038, B=149) +Fout=957.50 (A=039, B=149) +Fout=957.60 (A=040, B=149) +Fout=957.70 (A=041, B=149) +Fout=957.80 (A=042, B=149) +Fout=957.90 (A=043, B=149) +Fout=958.00 (A=044, B=149) +Fout=958.10 (A=045, B=149) +Fout=958.20 (A=046, B=149) +Fout=958.30 (A=047, B=149) +Fout=958.40 (A=048, B=149) +Fout=958.50 (A=049, B=149) +Fout=958.60 (A=050, B=149) +Fout=958.70 (A=051, B=149) +Fout=958.80 (A=052, B=149) +Fout=958.90 (A=053, B=149) +Fout=959.00 (A=054, B=149) +Fout=959.10 (A=055, B=149) +Fout=959.20 (A=056, B=149) +Fout=959.30 (A=057, B=149) +Fout=959.40 (A=058, B=149) +Fout=959.50 (A=059, B=149) +Fout=959.60 (A=060, B=149) +Fout=959.70 (A=061, B=149) +Fout=959.80 (A=062, B=149) +Fout=960.00 (A=000, B=150) +Fout=960.10 (A=001, B=150) +Fout=960.20 (A=002, B=150) +Fout=960.30 (A=003, B=150) +Fout=960.40 (A=004, B=150) +Fout=960.50 (A=005, B=150) +Fout=960.60 (A=006, B=150) +Fout=960.70 (A=007, B=150) +Fout=960.80 (A=008, B=150) +Fout=960.90 (A=009, B=150) +Fout=961.00 (A=010, B=150) +Fout=961.10 (A=011, B=150) +Fout=961.20 (A=012, B=150) +Fout=961.30 (A=013, B=150) +Fout=961.40 (A=014, B=150) +Fout=961.50 (A=015, B=150) +Fout=961.60 (A=016, B=150) +Fout=961.70 (A=017, B=150) +Fout=961.80 (A=018, B=150) +Fout=961.90 (A=019, B=150) +Fout=962.00 (A=020, B=150) +Fout=962.10 (A=021, B=150) +Fout=962.20 (A=022, B=150) +Fout=962.30 (A=023, B=150) +Fout=962.40 (A=024, B=150) +Fout=962.50 (A=025, B=150) +Fout=962.60 (A=026, B=150) +Fout=962.70 (A=027, B=150) +Fout=962.80 (A=028, B=150) +Fout=962.90 (A=029, B=150) +Fout=963.00 (A=030, B=150) +Fout=963.10 (A=031, B=150) +Fout=963.20 (A=032, B=150) +Fout=963.30 (A=033, B=150) +Fout=963.40 (A=034, B=150) +Fout=963.50 (A=035, B=150) +Fout=963.60 (A=036, B=150) +Fout=963.70 (A=037, B=150) +Fout=963.80 (A=038, B=150) +Fout=963.90 (A=039, B=150) +Fout=964.00 (A=040, B=150) +Fout=964.10 (A=041, B=150) +Fout=964.20 (A=042, B=150) +Fout=964.30 (A=043, B=150) +Fout=964.40 (A=044, B=150) +Fout=964.50 (A=045, B=150) +Fout=964.60 (A=046, B=150) +Fout=964.70 (A=047, B=150) +Fout=964.80 (A=048, B=150) +Fout=964.90 (A=049, B=150) +Fout=965.00 (A=050, B=150) +Fout=965.10 (A=051, B=150) +Fout=965.20 (A=052, B=150) +Fout=965.30 (A=053, B=150) +Fout=965.40 (A=054, B=150) +Fout=965.50 (A=055, B=150) +Fout=965.60 (A=056, B=150) +Fout=965.70 (A=057, B=150) +Fout=965.80 (A=058, B=150) +Fout=965.90 (A=059, B=150) +Fout=966.00 (A=060, B=150) +Fout=966.10 (A=061, B=150) +Fout=966.20 (A=062, B=150) +====================================================================== +PLL Rx High Band: +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1920.00 (A=000, B=150) +Fout=1920.20 (A=001, B=150) +Fout=1920.40 (A=002, B=150) +Fout=1920.60 (A=003, B=150) +Fout=1920.80 (A=004, B=150) +Fout=1921.00 (A=005, B=150) +Fout=1921.20 (A=006, B=150) +Fout=1921.40 (A=007, B=150) +Fout=1921.60 (A=008, B=150) +Fout=1921.80 (A=009, B=150) +Fout=1922.00 (A=010, B=150) +Fout=1922.20 (A=011, B=150) +Fout=1922.40 (A=012, B=150) +Fout=1922.60 (A=013, B=150) +Fout=1922.80 (A=014, B=150) +Fout=1923.00 (A=015, B=150) +Fout=1923.20 (A=016, B=150) +Fout=1923.40 (A=017, B=150) +Fout=1923.60 (A=018, B=150) +Fout=1923.80 (A=019, B=150) +Fout=1924.00 (A=020, B=150) +Fout=1924.20 (A=021, B=150) +Fout=1924.40 (A=022, B=150) +Fout=1924.60 (A=023, B=150) +Fout=1924.80 (A=024, B=150) +Fout=1925.00 (A=025, B=150) +Fout=1925.20 (A=026, B=150) +Fout=1925.40 (A=027, B=150) +Fout=1925.60 (A=028, B=150) +Fout=1925.80 (A=029, B=150) +Fout=1926.00 (A=030, B=150) +Fout=1926.20 (A=031, B=150) +Fout=1926.40 (A=032, B=150) +Fout=1926.60 (A=033, B=150) +Fout=1926.80 (A=034, B=150) +Fout=1927.00 (A=035, B=150) +Fout=1927.20 (A=036, B=150) +Fout=1927.40 (A=037, B=150) +Fout=1927.60 (A=038, B=150) +Fout=1927.80 (A=039, B=150) +Fout=1928.00 (A=040, B=150) +Fout=1928.20 (A=041, B=150) +Fout=1928.40 (A=042, B=150) +Fout=1928.60 (A=043, B=150) +Fout=1928.80 (A=044, B=150) +Fout=1929.00 (A=045, B=150) +Fout=1929.20 (A=046, B=150) +Fout=1929.40 (A=047, B=150) +Fout=1929.60 (A=048, B=150) +Fout=1929.80 (A=049, B=150) +Fout=1930.00 (A=050, B=150) +Fout=1930.20 (A=051, B=150) +Fout=1930.40 (A=052, B=150) +Fout=1930.60 (A=053, B=150) +Fout=1930.80 (A=054, B=150) +Fout=1931.00 (A=055, B=150) +Fout=1931.20 (A=056, B=150) +Fout=1931.40 (A=057, B=150) +Fout=1931.60 (A=058, B=150) +Fout=1931.80 (A=059, B=150) +Fout=1932.00 (A=060, B=150) +Fout=1932.20 (A=061, B=150) +Fout=1932.40 (A=062, B=150) +Fout=1932.80 (A=000, B=151) +Fout=1933.00 (A=001, B=151) +Fout=1933.20 (A=002, B=151) +Fout=1933.40 (A=003, B=151) +Fout=1933.60 (A=004, B=151) +Fout=1933.80 (A=005, B=151) +Fout=1934.00 (A=006, B=151) +Fout=1934.20 (A=007, B=151) +Fout=1934.40 (A=008, B=151) +Fout=1934.60 (A=009, B=151) +Fout=1934.80 (A=010, B=151) +Fout=1935.00 (A=011, B=151) +Fout=1935.20 (A=012, B=151) +Fout=1935.40 (A=013, B=151) +Fout=1935.60 (A=014, B=151) +Fout=1935.80 (A=015, B=151) +Fout=1936.00 (A=016, B=151) +Fout=1936.20 (A=017, B=151) +Fout=1936.40 (A=018, B=151) +Fout=1936.60 (A=019, B=151) +Fout=1936.80 (A=020, B=151) +Fout=1937.00 (A=021, B=151) +Fout=1937.20 (A=022, B=151) +Fout=1937.40 (A=023, B=151) +Fout=1937.60 (A=024, B=151) +Fout=1937.80 (A=025, B=151) +Fout=1938.00 (A=026, B=151) +Fout=1938.20 (A=027, B=151) +Fout=1938.40 (A=028, B=151) +Fout=1938.60 (A=029, B=151) +Fout=1938.80 (A=030, B=151) +Fout=1939.00 (A=031, B=151) +Fout=1939.20 (A=032, B=151) +Fout=1939.40 (A=033, B=151) +Fout=1939.60 (A=034, B=151) +Fout=1939.80 (A=035, B=151) +Fout=1940.00 (A=036, B=151) +Fout=1940.20 (A=037, B=151) +Fout=1940.40 (A=038, B=151) +Fout=1940.60 (A=039, B=151) +Fout=1940.80 (A=040, B=151) +Fout=1941.00 (A=041, B=151) +Fout=1941.20 (A=042, B=151) +Fout=1941.40 (A=043, B=151) +Fout=1941.60 (A=044, B=151) +Fout=1941.80 (A=045, B=151) +Fout=1942.00 (A=046, B=151) +Fout=1942.20 (A=047, B=151) +Fout=1942.40 (A=048, B=151) +Fout=1942.60 (A=049, B=151) +Fout=1942.80 (A=050, B=151) +Fout=1943.00 (A=051, B=151) +Fout=1943.20 (A=052, B=151) +Fout=1943.40 (A=053, B=151) +Fout=1943.60 (A=054, B=151) +Fout=1943.80 (A=055, B=151) +Fout=1944.00 (A=056, B=151) +Fout=1944.20 (A=057, B=151) +Fout=1944.40 (A=058, B=151) +Fout=1944.60 (A=059, B=151) +Fout=1944.80 (A=060, B=151) +Fout=1945.00 (A=061, B=151) +Fout=1945.20 (A=062, B=151) +Fout=1945.60 (A=000, B=152) +Fout=1945.80 (A=001, B=152) +Fout=1946.00 (A=002, B=152) +Fout=1946.20 (A=003, B=152) +Fout=1946.40 (A=004, B=152) +Fout=1946.60 (A=005, B=152) +Fout=1946.80 (A=006, B=152) +Fout=1947.00 (A=007, B=152) +Fout=1947.20 (A=008, B=152) +Fout=1947.40 (A=009, B=152) +Fout=1947.60 (A=010, B=152) +Fout=1947.80 (A=011, B=152) +Fout=1948.00 (A=012, B=152) +Fout=1948.20 (A=013, B=152) +Fout=1948.40 (A=014, B=152) +Fout=1948.60 (A=015, B=152) +Fout=1948.80 (A=016, B=152) +Fout=1949.00 (A=017, B=152) +Fout=1949.20 (A=018, B=152) +Fout=1949.40 (A=019, B=152) +Fout=1949.60 (A=020, B=152) +Fout=1949.80 (A=021, B=152) +Fout=1950.00 (A=022, B=152) +Fout=1950.20 (A=023, B=152) +Fout=1950.40 (A=024, B=152) +Fout=1950.60 (A=025, B=152) +Fout=1950.80 (A=026, B=152) +Fout=1951.00 (A=027, B=152) +Fout=1951.20 (A=028, B=152) +Fout=1951.40 (A=029, B=152) +Fout=1951.60 (A=030, B=152) +Fout=1951.80 (A=031, B=152) +Fout=1952.00 (A=032, B=152) +Fout=1952.20 (A=033, B=152) +Fout=1952.40 (A=034, B=152) +Fout=1952.60 (A=035, B=152) +Fout=1952.80 (A=036, B=152) +Fout=1953.00 (A=037, B=152) +Fout=1953.20 (A=038, B=152) +Fout=1953.40 (A=039, B=152) +Fout=1953.60 (A=040, B=152) +Fout=1953.80 (A=041, B=152) +Fout=1954.00 (A=042, B=152) +Fout=1954.20 (A=043, B=152) +Fout=1954.40 (A=044, B=152) +Fout=1954.60 (A=045, B=152) +Fout=1954.80 (A=046, B=152) +Fout=1955.00 (A=047, B=152) +Fout=1955.20 (A=048, B=152) +Fout=1955.40 (A=049, B=152) +Fout=1955.60 (A=050, B=152) +Fout=1955.80 (A=051, B=152) +Fout=1956.00 (A=052, B=152) +Fout=1956.20 (A=053, B=152) +Fout=1956.40 (A=054, B=152) +Fout=1956.60 (A=055, B=152) +Fout=1956.80 (A=056, B=152) +Fout=1957.00 (A=057, B=152) +Fout=1957.20 (A=058, B=152) +Fout=1957.40 (A=059, B=152) +Fout=1957.60 (A=060, B=152) +Fout=1957.80 (A=061, B=152) +Fout=1958.00 (A=062, B=152) +Fout=1958.40 (A=000, B=153) +Fout=1958.60 (A=001, B=153) +Fout=1958.80 (A=002, B=153) +Fout=1959.00 (A=003, B=153) +Fout=1959.20 (A=004, B=153) +Fout=1959.40 (A=005, B=153) +Fout=1959.60 (A=006, B=153) +Fout=1959.80 (A=007, B=153) +Fout=1960.00 (A=008, B=153) +Fout=1960.20 (A=009, B=153) +Fout=1960.40 (A=010, B=153) +Fout=1960.60 (A=011, B=153) +Fout=1960.80 (A=012, B=153) +Fout=1961.00 (A=013, B=153) +Fout=1961.20 (A=014, B=153) +Fout=1961.40 (A=015, B=153) +Fout=1961.60 (A=016, B=153) +Fout=1961.80 (A=017, B=153) +Fout=1962.00 (A=018, B=153) +Fout=1962.20 (A=019, B=153) +Fout=1962.40 (A=020, B=153) +Fout=1962.60 (A=021, B=153) +Fout=1962.80 (A=022, B=153) +Fout=1963.00 (A=023, B=153) +Fout=1963.20 (A=024, B=153) +Fout=1963.40 (A=025, B=153) +Fout=1963.60 (A=026, B=153) +Fout=1963.80 (A=027, B=153) +Fout=1964.00 (A=028, B=153) +Fout=1964.20 (A=029, B=153) +Fout=1964.40 (A=030, B=153) +Fout=1964.60 (A=031, B=153) +Fout=1964.80 (A=032, B=153) +Fout=1965.00 (A=033, B=153) +Fout=1965.20 (A=034, B=153) +Fout=1965.40 (A=035, B=153) +Fout=1965.60 (A=036, B=153) +Fout=1965.80 (A=037, B=153) +Fout=1966.00 (A=038, B=153) +Fout=1966.20 (A=039, B=153) +Fout=1966.40 (A=040, B=153) +Fout=1966.60 (A=041, B=153) +Fout=1966.80 (A=042, B=153) +Fout=1967.00 (A=043, B=153) +Fout=1967.20 (A=044, B=153) +Fout=1967.40 (A=045, B=153) +Fout=1967.60 (A=046, B=153) +Fout=1967.80 (A=047, B=153) +Fout=1968.00 (A=048, B=153) +Fout=1968.20 (A=049, B=153) +Fout=1968.40 (A=050, B=153) +Fout=1968.60 (A=051, B=153) +Fout=1968.80 (A=052, B=153) +Fout=1969.00 (A=053, B=153) +Fout=1969.20 (A=054, B=153) +Fout=1969.40 (A=055, B=153) +Fout=1969.60 (A=056, B=153) +Fout=1969.80 (A=057, B=153) +Fout=1970.00 (A=058, B=153) +Fout=1970.20 (A=059, B=153) +Fout=1970.40 (A=060, B=153) +Fout=1970.60 (A=061, B=153) +Fout=1970.80 (A=062, B=153) +Fout=1971.20 (A=000, B=154) +Fout=1971.40 (A=001, B=154) +Fout=1971.60 (A=002, B=154) +Fout=1971.80 (A=003, B=154) +Fout=1972.00 (A=004, B=154) +Fout=1972.20 (A=005, B=154) +Fout=1972.40 (A=006, B=154) +Fout=1972.60 (A=007, B=154) +Fout=1972.80 (A=008, B=154) +Fout=1973.00 (A=009, B=154) +Fout=1973.20 (A=010, B=154) +Fout=1973.40 (A=011, B=154) +Fout=1973.60 (A=012, B=154) +Fout=1973.80 (A=013, B=154) +Fout=1974.00 (A=014, B=154) +Fout=1974.20 (A=015, B=154) +Fout=1974.40 (A=016, B=154) +Fout=1974.60 (A=017, B=154) +Fout=1974.80 (A=018, B=154) +Fout=1975.00 (A=019, B=154) +Fout=1975.20 (A=020, B=154) +Fout=1975.40 (A=021, B=154) +Fout=1975.60 (A=022, B=154) +Fout=1975.80 (A=023, B=154) +Fout=1976.00 (A=024, B=154) +Fout=1976.20 (A=025, B=154) +Fout=1976.40 (A=026, B=154) +Fout=1976.60 (A=027, B=154) +Fout=1976.80 (A=028, B=154) +Fout=1977.00 (A=029, B=154) +Fout=1977.20 (A=030, B=154) +Fout=1977.40 (A=031, B=154) +Fout=1977.60 (A=032, B=154) +Fout=1977.80 (A=033, B=154) +Fout=1978.00 (A=034, B=154) +Fout=1978.20 (A=035, B=154) +Fout=1978.40 (A=036, B=154) +Fout=1978.60 (A=037, B=154) +Fout=1978.80 (A=038, B=154) +Fout=1979.00 (A=039, B=154) +Fout=1979.20 (A=040, B=154) +Fout=1979.40 (A=041, B=154) +Fout=1979.60 (A=042, B=154) +Fout=1979.80 (A=043, B=154) +Fout=1980.00 (A=044, B=154) +Fout=1980.20 (A=045, B=154) +Fout=1980.40 (A=046, B=154) +Fout=1980.60 (A=047, B=154) +Fout=1980.80 (A=048, B=154) +Fout=1981.00 (A=049, B=154) +Fout=1981.20 (A=050, B=154) +Fout=1981.40 (A=051, B=154) +Fout=1981.60 (A=052, B=154) +Fout=1981.80 (A=053, B=154) +Fout=1982.00 (A=054, B=154) +Fout=1982.20 (A=055, B=154) +Fout=1982.40 (A=056, B=154) +Fout=1982.60 (A=057, B=154) +Fout=1982.80 (A=058, B=154) +Fout=1983.00 (A=059, B=154) +Fout=1983.20 (A=060, B=154) +Fout=1983.40 (A=061, B=154) +Fout=1983.60 (A=062, B=154) +Fout=1984.00 (A=000, B=155) +Fout=1984.20 (A=001, B=155) +Fout=1984.40 (A=002, B=155) +Fout=1984.60 (A=003, B=155) +Fout=1984.80 (A=004, B=155) +Fout=1985.00 (A=005, B=155) +Fout=1985.20 (A=006, B=155) +Fout=1985.40 (A=007, B=155) +Fout=1985.60 (A=008, B=155) +Fout=1985.80 (A=009, B=155) +Fout=1986.00 (A=010, B=155) +Fout=1986.20 (A=011, B=155) +Fout=1986.40 (A=012, B=155) +Fout=1986.60 (A=013, B=155) +Fout=1986.80 (A=014, B=155) +Fout=1987.00 (A=015, B=155) +Fout=1987.20 (A=016, B=155) +Fout=1987.40 (A=017, B=155) +Fout=1987.60 (A=018, B=155) +Fout=1987.80 (A=019, B=155) +Fout=1988.00 (A=020, B=155) +Fout=1988.20 (A=021, B=155) +Fout=1988.40 (A=022, B=155) +Fout=1988.60 (A=023, B=155) +Fout=1988.80 (A=024, B=155) +Fout=1989.00 (A=025, B=155) +Fout=1989.20 (A=026, B=155) +Fout=1989.40 (A=027, B=155) +Fout=1989.60 (A=028, B=155) +Fout=1989.80 (A=029, B=155) +Fout=1990.00 (A=030, B=155) +Fout=1990.20 (A=031, B=155) +Fout=1990.40 (A=032, B=155) +Fout=1990.60 (A=033, B=155) +Fout=1990.80 (A=034, B=155) +Fout=1991.00 (A=035, B=155) +Fout=1991.20 (A=036, B=155) +Fout=1991.40 (A=037, B=155) +Fout=1991.60 (A=038, B=155) +Fout=1991.80 (A=039, B=155) +Fout=1992.00 (A=040, B=155) +Fout=1992.20 (A=041, B=155) +Fout=1992.40 (A=042, B=155) +Fout=1992.60 (A=043, B=155) +Fout=1992.80 (A=044, B=155) +Fout=1993.00 (A=045, B=155) +Fout=1993.20 (A=046, B=155) +Fout=1993.40 (A=047, B=155) +Fout=1993.60 (A=048, B=155) +Fout=1993.80 (A=049, B=155) +Fout=1994.00 (A=050, B=155) +Fout=1994.20 (A=051, B=155) +Fout=1994.40 (A=052, B=155) +Fout=1994.60 (A=053, B=155) +Fout=1994.80 (A=054, B=155) +Fout=1995.00 (A=055, B=155) +Fout=1995.20 (A=056, B=155) +Fout=1995.40 (A=057, B=155) +Fout=1995.60 (A=058, B=155) +Fout=1995.80 (A=059, B=155) +Fout=1996.00 (A=060, B=155) +Fout=1996.20 (A=061, B=155) +Fout=1996.40 (A=062, B=155) +====================================================================== +PLL Tx GSM850_1 +Fout=819.20 (A=000, B=128) +Fout=819.30 (A=001, B=128) +Fout=819.40 (A=002, B=128) +Fout=819.50 (A=003, B=128) +Fout=819.60 (A=004, B=128) +Fout=819.70 (A=005, B=128) +Fout=819.80 (A=006, B=128) +Fout=819.90 (A=007, B=128) +Fout=820.00 (A=008, B=128) +Fout=820.10 (A=009, B=128) +Fout=820.20 (A=010, B=128) +Fout=820.30 (A=011, B=128) +Fout=820.40 (A=012, B=128) +Fout=820.50 (A=013, B=128) +Fout=820.60 (A=014, B=128) +Fout=820.70 (A=015, B=128) +Fout=820.80 (A=016, B=128) +Fout=820.90 (A=017, B=128) +Fout=821.00 (A=018, B=128) +Fout=821.10 (A=019, B=128) +Fout=821.20 (A=020, B=128) +Fout=821.30 (A=021, B=128) +Fout=821.40 (A=022, B=128) +Fout=821.50 (A=023, B=128) +Fout=821.60 (A=024, B=128) +Fout=821.70 (A=025, B=128) +Fout=821.80 (A=026, B=128) +Fout=821.90 (A=027, B=128) +Fout=822.00 (A=028, B=128) +Fout=822.10 (A=029, B=128) +Fout=822.20 (A=030, B=128) +Fout=822.30 (A=031, B=128) +Fout=822.40 (A=032, B=128) +Fout=822.50 (A=033, B=128) +Fout=822.60 (A=034, B=128) +Fout=822.70 (A=035, B=128) +Fout=822.80 (A=036, B=128) +Fout=822.90 (A=037, B=128) +Fout=823.00 (A=038, B=128) +Fout=823.10 (A=039, B=128) +Fout=823.20 (A=040, B=128) +Fout=823.30 (A=041, B=128) +Fout=823.40 (A=042, B=128) +Fout=823.50 (A=043, B=128) +Fout=823.60 (A=044, B=128) +Fout=823.70 (A=045, B=128) +Fout=823.80 (A=046, B=128) +Fout=823.90 (A=047, B=128) +Fout=824.00 (A=048, B=128) +Fout=824.10 (A=049, B=128) +Fout=824.20 (A=050, B=128) +Fout=824.30 (A=051, B=128) +Fout=824.40 (A=052, B=128) +Fout=824.50 (A=053, B=128) +Fout=824.60 (A=054, B=128) +Fout=824.70 (A=055, B=128) +Fout=824.80 (A=056, B=128) +Fout=824.90 (A=057, B=128) +Fout=825.00 (A=058, B=128) +Fout=825.10 (A=059, B=128) +Fout=825.20 (A=060, B=128) +Fout=825.30 (A=061, B=128) +Fout=825.40 (A=062, B=128) +Fout=825.60 (A=000, B=129) +Fout=825.70 (A=001, B=129) +Fout=825.80 (A=002, B=129) +Fout=825.90 (A=003, B=129) +Fout=826.00 (A=004, B=129) +Fout=826.10 (A=005, B=129) +Fout=826.20 (A=006, B=129) +Fout=826.30 (A=007, B=129) +Fout=826.40 (A=008, B=129) +Fout=826.50 (A=009, B=129) +Fout=826.60 (A=010, B=129) +Fout=826.70 (A=011, B=129) +Fout=826.80 (A=012, B=129) +Fout=826.90 (A=013, B=129) +Fout=827.00 (A=014, B=129) +Fout=827.10 (A=015, B=129) +Fout=827.20 (A=016, B=129) +Fout=827.30 (A=017, B=129) +Fout=827.40 (A=018, B=129) +Fout=827.50 (A=019, B=129) +Fout=827.60 (A=020, B=129) +Fout=827.70 (A=021, B=129) +Fout=827.80 (A=022, B=129) +Fout=827.90 (A=023, B=129) +Fout=828.00 (A=024, B=129) +Fout=828.10 (A=025, B=129) +Fout=828.20 (A=026, B=129) +Fout=828.30 (A=027, B=129) +Fout=828.40 (A=028, B=129) +Fout=828.50 (A=029, B=129) +Fout=828.60 (A=030, B=129) +Fout=828.70 (A=031, B=129) +Fout=828.80 (A=032, B=129) +Fout=828.90 (A=033, B=129) +Fout=829.00 (A=034, B=129) +Fout=829.10 (A=035, B=129) +Fout=829.20 (A=036, B=129) +Fout=829.30 (A=037, B=129) +Fout=829.40 (A=038, B=129) +Fout=829.50 (A=039, B=129) +Fout=829.60 (A=040, B=129) +Fout=829.70 (A=041, B=129) +Fout=829.80 (A=042, B=129) +Fout=829.90 (A=043, B=129) +Fout=830.00 (A=044, B=129) +Fout=830.10 (A=045, B=129) +Fout=830.20 (A=046, B=129) +Fout=830.30 (A=047, B=129) +Fout=830.40 (A=048, B=129) +Fout=830.50 (A=049, B=129) +Fout=830.60 (A=050, B=129) +Fout=830.70 (A=051, B=129) +Fout=830.80 (A=052, B=129) +Fout=830.90 (A=053, B=129) +Fout=831.00 (A=054, B=129) +Fout=831.10 (A=055, B=129) +Fout=831.20 (A=056, B=129) +Fout=831.30 (A=057, B=129) +Fout=831.40 (A=058, B=129) +Fout=831.50 (A=059, B=129) +Fout=831.60 (A=060, B=129) +Fout=831.70 (A=061, B=129) +Fout=831.80 (A=062, B=129) +Fout=832.00 (A=000, B=130) +Fout=832.10 (A=001, B=130) +Fout=832.20 (A=002, B=130) +Fout=832.30 (A=003, B=130) +Fout=832.40 (A=004, B=130) +Fout=832.50 (A=005, B=130) +Fout=832.60 (A=006, B=130) +Fout=832.70 (A=007, B=130) +Fout=832.80 (A=008, B=130) +Fout=832.90 (A=009, B=130) +Fout=833.00 (A=010, B=130) +Fout=833.10 (A=011, B=130) +Fout=833.20 (A=012, B=130) +Fout=833.30 (A=013, B=130) +Fout=833.40 (A=014, B=130) +Fout=833.50 (A=015, B=130) +Fout=833.60 (A=016, B=130) +Fout=833.70 (A=017, B=130) +Fout=833.80 (A=018, B=130) +Fout=833.90 (A=019, B=130) +Fout=834.00 (A=020, B=130) +Fout=834.10 (A=021, B=130) +Fout=834.20 (A=022, B=130) +Fout=834.30 (A=023, B=130) +Fout=834.40 (A=024, B=130) +Fout=834.50 (A=025, B=130) +Fout=834.60 (A=026, B=130) +Fout=834.70 (A=027, B=130) +Fout=834.80 (A=028, B=130) +Fout=834.90 (A=029, B=130) +Fout=835.00 (A=030, B=130) +Fout=835.10 (A=031, B=130) +Fout=835.20 (A=032, B=130) +Fout=835.30 (A=033, B=130) +Fout=835.40 (A=034, B=130) +Fout=835.50 (A=035, B=130) +Fout=835.60 (A=036, B=130) +Fout=835.70 (A=037, B=130) +Fout=835.80 (A=038, B=130) +Fout=835.90 (A=039, B=130) +Fout=836.00 (A=040, B=130) +Fout=836.10 (A=041, B=130) +Fout=836.20 (A=042, B=130) +Fout=836.30 (A=043, B=130) +Fout=836.40 (A=044, B=130) +Fout=836.50 (A=045, B=130) +Fout=836.60 (A=046, B=130) +Fout=836.70 (A=047, B=130) +Fout=836.80 (A=048, B=130) +Fout=836.90 (A=049, B=130) +Fout=837.00 (A=050, B=130) +Fout=837.10 (A=051, B=130) +Fout=837.20 (A=052, B=130) +Fout=837.30 (A=053, B=130) +Fout=837.40 (A=054, B=130) +Fout=837.50 (A=055, B=130) +Fout=837.60 (A=056, B=130) +Fout=837.70 (A=057, B=130) +Fout=837.80 (A=058, B=130) +Fout=837.90 (A=059, B=130) +Fout=838.00 (A=060, B=130) +Fout=838.10 (A=061, B=130) +Fout=838.20 (A=062, B=130) +====================================================================== +PLL Tx GSM850_2 +Fout=832.00 (A=000, B=065) +Fout=832.20 (A=001, B=065) +Fout=832.40 (A=002, B=065) +Fout=832.60 (A=003, B=065) +Fout=832.80 (A=004, B=065) +Fout=833.00 (A=005, B=065) +Fout=833.20 (A=006, B=065) +Fout=833.40 (A=007, B=065) +Fout=833.60 (A=008, B=065) +Fout=833.80 (A=009, B=065) +Fout=834.00 (A=010, B=065) +Fout=834.20 (A=011, B=065) +Fout=834.40 (A=012, B=065) +Fout=834.60 (A=013, B=065) +Fout=834.80 (A=014, B=065) +Fout=835.00 (A=015, B=065) +Fout=835.20 (A=016, B=065) +Fout=835.40 (A=017, B=065) +Fout=835.60 (A=018, B=065) +Fout=835.80 (A=019, B=065) +Fout=836.00 (A=020, B=065) +Fout=836.20 (A=021, B=065) +Fout=836.40 (A=022, B=065) +Fout=836.60 (A=023, B=065) +Fout=836.80 (A=024, B=065) +Fout=837.00 (A=025, B=065) +Fout=837.20 (A=026, B=065) +Fout=837.40 (A=027, B=065) +Fout=837.60 (A=028, B=065) +Fout=837.80 (A=029, B=065) +Fout=838.00 (A=030, B=065) +Fout=838.20 (A=031, B=065) +Fout=838.40 (A=032, B=065) +Fout=838.60 (A=033, B=065) +Fout=838.80 (A=034, B=065) +Fout=839.00 (A=035, B=065) +Fout=839.20 (A=036, B=065) +Fout=839.40 (A=037, B=065) +Fout=839.60 (A=038, B=065) +Fout=839.80 (A=039, B=065) +Fout=840.00 (A=040, B=065) +Fout=840.20 (A=041, B=065) +Fout=840.40 (A=042, B=065) +Fout=840.60 (A=043, B=065) +Fout=840.80 (A=044, B=065) +Fout=841.00 (A=045, B=065) +Fout=841.20 (A=046, B=065) +Fout=841.40 (A=047, B=065) +Fout=841.60 (A=048, B=065) +Fout=841.80 (A=049, B=065) +Fout=842.00 (A=050, B=065) +Fout=842.20 (A=051, B=065) +Fout=842.40 (A=052, B=065) +Fout=842.60 (A=053, B=065) +Fout=842.80 (A=054, B=065) +Fout=843.00 (A=055, B=065) +Fout=843.20 (A=056, B=065) +Fout=843.40 (A=057, B=065) +Fout=843.60 (A=058, B=065) +Fout=843.80 (A=059, B=065) +Fout=844.00 (A=060, B=065) +Fout=844.20 (A=061, B=065) +Fout=844.40 (A=062, B=065) +Fout=844.60 (A=063, B=065) +Fout=844.80 (A=000, B=066) +Fout=845.00 (A=001, B=066) +Fout=845.20 (A=002, B=066) +Fout=845.40 (A=003, B=066) +Fout=845.60 (A=004, B=066) +Fout=845.80 (A=005, B=066) +Fout=846.00 (A=006, B=066) +Fout=846.20 (A=007, B=066) +Fout=846.40 (A=008, B=066) +Fout=846.60 (A=009, B=066) +Fout=846.80 (A=010, B=066) +Fout=847.00 (A=011, B=066) +Fout=847.20 (A=012, B=066) +Fout=847.40 (A=013, B=066) +Fout=847.60 (A=014, B=066) +Fout=847.80 (A=015, B=066) +Fout=848.00 (A=016, B=066) +Fout=848.20 (A=017, B=066) +Fout=848.40 (A=018, B=066) +Fout=848.60 (A=019, B=066) +Fout=848.80 (A=020, B=066) +Fout=849.00 (A=021, B=066) +Fout=849.20 (A=022, B=066) +Fout=849.40 (A=023, B=066) +Fout=849.60 (A=024, B=066) +Fout=849.80 (A=025, B=066) +Fout=850.00 (A=026, B=066) +Fout=850.20 (A=027, B=066) +Fout=850.40 (A=028, B=066) +Fout=850.60 (A=029, B=066) +Fout=850.80 (A=030, B=066) +Fout=851.00 (A=031, B=066) +Fout=851.20 (A=032, B=066) +Fout=851.40 (A=033, B=066) +Fout=851.60 (A=034, B=066) +Fout=851.80 (A=035, B=066) +Fout=852.00 (A=036, B=066) +Fout=852.20 (A=037, B=066) +Fout=852.40 (A=038, B=066) +Fout=852.60 (A=039, B=066) +Fout=852.80 (A=040, B=066) +Fout=853.00 (A=041, B=066) +Fout=853.20 (A=042, B=066) +Fout=853.40 (A=043, B=066) +Fout=853.60 (A=044, B=066) +Fout=853.80 (A=045, B=066) +Fout=854.00 (A=046, B=066) +Fout=854.20 (A=047, B=066) +Fout=854.40 (A=048, B=066) +Fout=854.60 (A=049, B=066) +Fout=854.80 (A=050, B=066) +Fout=855.00 (A=051, B=066) +Fout=855.20 (A=052, B=066) +Fout=855.40 (A=053, B=066) +Fout=855.60 (A=054, B=066) +Fout=855.80 (A=055, B=066) +Fout=856.00 (A=056, B=066) +Fout=856.20 (A=057, B=066) +Fout=856.40 (A=058, B=066) +Fout=856.60 (A=059, B=066) +Fout=856.80 (A=060, B=066) +Fout=857.00 (A=061, B=066) +Fout=857.20 (A=062, B=066) +Fout=857.40 (A=063, B=066) +====================================================================== +PLL Tx GSM900 +Fout=870.40 (A=000, B=068) +Fout=870.60 (A=001, B=068) +Fout=870.80 (A=002, B=068) +Fout=871.00 (A=003, B=068) +Fout=871.20 (A=004, B=068) +Fout=871.40 (A=005, B=068) +Fout=871.60 (A=006, B=068) +Fout=871.80 (A=007, B=068) +Fout=872.00 (A=008, B=068) +Fout=872.20 (A=009, B=068) +Fout=872.40 (A=010, B=068) +Fout=872.60 (A=011, B=068) +Fout=872.80 (A=012, B=068) +Fout=873.00 (A=013, B=068) +Fout=873.20 (A=014, B=068) +Fout=873.40 (A=015, B=068) +Fout=873.60 (A=016, B=068) +Fout=873.80 (A=017, B=068) +Fout=874.00 (A=018, B=068) +Fout=874.20 (A=019, B=068) +Fout=874.40 (A=020, B=068) +Fout=874.60 (A=021, B=068) +Fout=874.80 (A=022, B=068) +Fout=875.00 (A=023, B=068) +Fout=875.20 (A=024, B=068) +Fout=875.40 (A=025, B=068) +Fout=875.60 (A=026, B=068) +Fout=875.80 (A=027, B=068) +Fout=876.00 (A=028, B=068) +Fout=876.20 (A=029, B=068) +Fout=876.40 (A=030, B=068) +Fout=876.60 (A=031, B=068) +Fout=876.80 (A=032, B=068) +Fout=877.00 (A=033, B=068) +Fout=877.20 (A=034, B=068) +Fout=877.40 (A=035, B=068) +Fout=877.60 (A=036, B=068) +Fout=877.80 (A=037, B=068) +Fout=878.00 (A=038, B=068) +Fout=878.20 (A=039, B=068) +Fout=878.40 (A=040, B=068) +Fout=878.60 (A=041, B=068) +Fout=878.80 (A=042, B=068) +Fout=879.00 (A=043, B=068) +Fout=879.20 (A=044, B=068) +Fout=879.40 (A=045, B=068) +Fout=879.60 (A=046, B=068) +Fout=879.80 (A=047, B=068) +Fout=880.00 (A=048, B=068) +Fout=880.20 (A=049, B=068) +Fout=880.40 (A=050, B=068) +Fout=880.60 (A=051, B=068) +Fout=880.80 (A=052, B=068) +Fout=881.00 (A=053, B=068) +Fout=881.20 (A=054, B=068) +Fout=881.40 (A=055, B=068) +Fout=881.60 (A=056, B=068) +Fout=881.80 (A=057, B=068) +Fout=882.00 (A=058, B=068) +Fout=882.20 (A=059, B=068) +Fout=882.40 (A=060, B=068) +Fout=882.60 (A=061, B=068) +Fout=882.80 (A=062, B=068) +Fout=883.00 (A=063, B=068) +Fout=883.20 (A=000, B=069) +Fout=883.40 (A=001, B=069) +Fout=883.60 (A=002, B=069) +Fout=883.80 (A=003, B=069) +Fout=884.00 (A=004, B=069) +Fout=884.20 (A=005, B=069) +Fout=884.40 (A=006, B=069) +Fout=884.60 (A=007, B=069) +Fout=884.80 (A=008, B=069) +Fout=885.00 (A=009, B=069) +Fout=885.20 (A=010, B=069) +Fout=885.40 (A=011, B=069) +Fout=885.60 (A=012, B=069) +Fout=885.80 (A=013, B=069) +Fout=886.00 (A=014, B=069) +Fout=886.20 (A=015, B=069) +Fout=886.40 (A=016, B=069) +Fout=886.60 (A=017, B=069) +Fout=886.80 (A=018, B=069) +Fout=887.00 (A=019, B=069) +Fout=887.20 (A=020, B=069) +Fout=887.40 (A=021, B=069) +Fout=887.60 (A=022, B=069) +Fout=887.80 (A=023, B=069) +Fout=888.00 (A=024, B=069) +Fout=888.20 (A=025, B=069) +Fout=888.40 (A=026, B=069) +Fout=888.60 (A=027, B=069) +Fout=888.80 (A=028, B=069) +Fout=889.00 (A=029, B=069) +Fout=889.20 (A=030, B=069) +Fout=889.40 (A=031, B=069) +Fout=889.60 (A=032, B=069) +Fout=889.80 (A=033, B=069) +Fout=890.00 (A=034, B=069) +Fout=890.20 (A=035, B=069) +Fout=890.40 (A=036, B=069) +Fout=890.60 (A=037, B=069) +Fout=890.80 (A=038, B=069) +Fout=891.00 (A=039, B=069) +Fout=891.20 (A=040, B=069) +Fout=891.40 (A=041, B=069) +Fout=891.60 (A=042, B=069) +Fout=891.80 (A=043, B=069) +Fout=892.00 (A=044, B=069) +Fout=892.20 (A=045, B=069) +Fout=892.40 (A=046, B=069) +Fout=892.60 (A=047, B=069) +Fout=892.80 (A=048, B=069) +Fout=893.00 (A=049, B=069) +Fout=893.20 (A=050, B=069) +Fout=893.40 (A=051, B=069) +Fout=893.60 (A=052, B=069) +Fout=893.80 (A=053, B=069) +Fout=894.00 (A=054, B=069) +Fout=894.20 (A=055, B=069) +Fout=894.40 (A=056, B=069) +Fout=894.60 (A=057, B=069) +Fout=894.80 (A=058, B=069) +Fout=895.00 (A=059, B=069) +Fout=895.20 (A=060, B=069) +Fout=895.40 (A=061, B=069) +Fout=895.60 (A=062, B=069) +Fout=895.80 (A=063, B=069) +Fout=896.00 (A=000, B=070) +Fout=896.20 (A=001, B=070) +Fout=896.40 (A=002, B=070) +Fout=896.60 (A=003, B=070) +Fout=896.80 (A=004, B=070) +Fout=897.00 (A=005, B=070) +Fout=897.20 (A=006, B=070) +Fout=897.40 (A=007, B=070) +Fout=897.60 (A=008, B=070) +Fout=897.80 (A=009, B=070) +Fout=898.00 (A=010, B=070) +Fout=898.20 (A=011, B=070) +Fout=898.40 (A=012, B=070) +Fout=898.60 (A=013, B=070) +Fout=898.80 (A=014, B=070) +Fout=899.00 (A=015, B=070) +Fout=899.20 (A=016, B=070) +Fout=899.40 (A=017, B=070) +Fout=899.60 (A=018, B=070) +Fout=899.80 (A=019, B=070) +Fout=900.00 (A=020, B=070) +Fout=900.20 (A=021, B=070) +Fout=900.40 (A=022, B=070) +Fout=900.60 (A=023, B=070) +Fout=900.80 (A=024, B=070) +Fout=901.00 (A=025, B=070) +Fout=901.20 (A=026, B=070) +Fout=901.40 (A=027, B=070) +Fout=901.60 (A=028, B=070) +Fout=901.80 (A=029, B=070) +Fout=902.00 (A=030, B=070) +Fout=902.20 (A=031, B=070) +Fout=902.40 (A=032, B=070) +Fout=902.60 (A=033, B=070) +Fout=902.80 (A=034, B=070) +Fout=903.00 (A=035, B=070) +Fout=903.20 (A=036, B=070) +Fout=903.40 (A=037, B=070) +Fout=903.60 (A=038, B=070) +Fout=903.80 (A=039, B=070) +Fout=904.00 (A=040, B=070) +Fout=904.20 (A=041, B=070) +Fout=904.40 (A=042, B=070) +Fout=904.60 (A=043, B=070) +Fout=904.80 (A=044, B=070) +Fout=905.00 (A=045, B=070) +Fout=905.20 (A=046, B=070) +Fout=905.40 (A=047, B=070) +Fout=905.60 (A=048, B=070) +Fout=905.80 (A=049, B=070) +Fout=906.00 (A=050, B=070) +Fout=906.20 (A=051, B=070) +Fout=906.40 (A=052, B=070) +Fout=906.60 (A=053, B=070) +Fout=906.80 (A=054, B=070) +Fout=907.00 (A=055, B=070) +Fout=907.20 (A=056, B=070) +Fout=907.40 (A=057, B=070) +Fout=907.60 (A=058, B=070) +Fout=907.80 (A=059, B=070) +Fout=908.00 (A=060, B=070) +Fout=908.20 (A=061, B=070) +Fout=908.40 (A=062, B=070) +Fout=908.60 (A=063, B=070) +Fout=908.80 (A=000, B=071) +Fout=909.00 (A=001, B=071) +Fout=909.20 (A=002, B=071) +Fout=909.40 (A=003, B=071) +Fout=909.60 (A=004, B=071) +Fout=909.80 (A=005, B=071) +Fout=910.00 (A=006, B=071) +Fout=910.20 (A=007, B=071) +Fout=910.40 (A=008, B=071) +Fout=910.60 (A=009, B=071) +Fout=910.80 (A=010, B=071) +Fout=911.00 (A=011, B=071) +Fout=911.20 (A=012, B=071) +Fout=911.40 (A=013, B=071) +Fout=911.60 (A=014, B=071) +Fout=911.80 (A=015, B=071) +Fout=912.00 (A=016, B=071) +Fout=912.20 (A=017, B=071) +Fout=912.40 (A=018, B=071) +Fout=912.60 (A=019, B=071) +Fout=912.80 (A=020, B=071) +Fout=913.00 (A=021, B=071) +Fout=913.20 (A=022, B=071) +Fout=913.40 (A=023, B=071) +Fout=913.60 (A=024, B=071) +Fout=913.80 (A=025, B=071) +Fout=914.00 (A=026, B=071) +Fout=914.20 (A=027, B=071) +Fout=914.40 (A=028, B=071) +Fout=914.60 (A=029, B=071) +Fout=914.80 (A=030, B=071) +Fout=915.00 (A=031, B=071) +Fout=915.20 (A=032, B=071) +Fout=915.40 (A=033, B=071) +Fout=915.60 (A=034, B=071) +Fout=915.80 (A=035, B=071) +Fout=916.00 (A=036, B=071) +Fout=916.20 (A=037, B=071) +Fout=916.40 (A=038, B=071) +Fout=916.60 (A=039, B=071) +Fout=916.80 (A=040, B=071) +Fout=917.00 (A=041, B=071) +Fout=917.20 (A=042, B=071) +Fout=917.40 (A=043, B=071) +Fout=917.60 (A=044, B=071) +Fout=917.80 (A=045, B=071) +Fout=918.00 (A=046, B=071) +Fout=918.20 (A=047, B=071) +Fout=918.40 (A=048, B=071) +Fout=918.60 (A=049, B=071) +Fout=918.80 (A=050, B=071) +Fout=919.00 (A=051, B=071) +Fout=919.20 (A=052, B=071) +Fout=919.40 (A=053, B=071) +Fout=919.60 (A=054, B=071) +Fout=919.80 (A=055, B=071) +Fout=920.00 (A=056, B=071) +Fout=920.20 (A=057, B=071) +Fout=920.40 (A=058, B=071) +Fout=920.60 (A=059, B=071) +Fout=920.80 (A=060, B=071) +Fout=921.00 (A=061, B=071) +Fout=921.20 (A=062, B=071) +Fout=921.40 (A=063, B=071) +====================================================================== +PLL Tx GSM1800/1900 +Fout=1702.40 (A=000, B=133) +Fout=1702.60 (A=001, B=133) +Fout=1702.80 (A=002, B=133) +Fout=1703.00 (A=003, B=133) +Fout=1703.20 (A=004, B=133) +Fout=1703.40 (A=005, B=133) +Fout=1703.60 (A=006, B=133) +Fout=1703.80 (A=007, B=133) +Fout=1704.00 (A=008, B=133) +Fout=1704.20 (A=009, B=133) +Fout=1704.40 (A=010, B=133) +Fout=1704.60 (A=011, B=133) +Fout=1704.80 (A=012, B=133) +Fout=1705.00 (A=013, B=133) +Fout=1705.20 (A=014, B=133) +Fout=1705.40 (A=015, B=133) +Fout=1705.60 (A=016, B=133) +Fout=1705.80 (A=017, B=133) +Fout=1706.00 (A=018, B=133) +Fout=1706.20 (A=019, B=133) +Fout=1706.40 (A=020, B=133) +Fout=1706.60 (A=021, B=133) +Fout=1706.80 (A=022, B=133) +Fout=1707.00 (A=023, B=133) +Fout=1707.20 (A=024, B=133) +Fout=1707.40 (A=025, B=133) +Fout=1707.60 (A=026, B=133) +Fout=1707.80 (A=027, B=133) +Fout=1708.00 (A=028, B=133) +Fout=1708.20 (A=029, B=133) +Fout=1708.40 (A=030, B=133) +Fout=1708.60 (A=031, B=133) +Fout=1708.80 (A=032, B=133) +Fout=1709.00 (A=033, B=133) +Fout=1709.20 (A=034, B=133) +Fout=1709.40 (A=035, B=133) +Fout=1709.60 (A=036, B=133) +Fout=1709.80 (A=037, B=133) +Fout=1710.00 (A=038, B=133) +Fout=1710.20 (A=039, B=133) +Fout=1710.40 (A=040, B=133) +Fout=1710.60 (A=041, B=133) +Fout=1710.80 (A=042, B=133) +Fout=1711.00 (A=043, B=133) +Fout=1711.20 (A=044, B=133) +Fout=1711.40 (A=045, B=133) +Fout=1711.60 (A=046, B=133) +Fout=1711.80 (A=047, B=133) +Fout=1712.00 (A=048, B=133) +Fout=1712.20 (A=049, B=133) +Fout=1712.40 (A=050, B=133) +Fout=1712.60 (A=051, B=133) +Fout=1712.80 (A=052, B=133) +Fout=1713.00 (A=053, B=133) +Fout=1713.20 (A=054, B=133) +Fout=1713.40 (A=055, B=133) +Fout=1713.60 (A=056, B=133) +Fout=1713.80 (A=057, B=133) +Fout=1714.00 (A=058, B=133) +Fout=1714.20 (A=059, B=133) +Fout=1714.40 (A=060, B=133) +Fout=1714.60 (A=061, B=133) +Fout=1714.80 (A=062, B=133) +Fout=1715.00 (A=063, B=133) +Fout=1715.20 (A=000, B=134) +Fout=1715.40 (A=001, B=134) +Fout=1715.60 (A=002, B=134) +Fout=1715.80 (A=003, B=134) +Fout=1716.00 (A=004, B=134) +Fout=1716.20 (A=005, B=134) +Fout=1716.40 (A=006, B=134) +Fout=1716.60 (A=007, B=134) +Fout=1716.80 (A=008, B=134) +Fout=1717.00 (A=009, B=134) +Fout=1717.20 (A=010, B=134) +Fout=1717.40 (A=011, B=134) +Fout=1717.60 (A=012, B=134) +Fout=1717.80 (A=013, B=134) +Fout=1718.00 (A=014, B=134) +Fout=1718.20 (A=015, B=134) +Fout=1718.40 (A=016, B=134) +Fout=1718.60 (A=017, B=134) +Fout=1718.80 (A=018, B=134) +Fout=1719.00 (A=019, B=134) +Fout=1719.20 (A=020, B=134) +Fout=1719.40 (A=021, B=134) +Fout=1719.60 (A=022, B=134) +Fout=1719.80 (A=023, B=134) +Fout=1720.00 (A=024, B=134) +Fout=1720.20 (A=025, B=134) +Fout=1720.40 (A=026, B=134) +Fout=1720.60 (A=027, B=134) +Fout=1720.80 (A=028, B=134) +Fout=1721.00 (A=029, B=134) +Fout=1721.20 (A=030, B=134) +Fout=1721.40 (A=031, B=134) +Fout=1721.60 (A=032, B=134) +Fout=1721.80 (A=033, B=134) +Fout=1722.00 (A=034, B=134) +Fout=1722.20 (A=035, B=134) +Fout=1722.40 (A=036, B=134) +Fout=1722.60 (A=037, B=134) +Fout=1722.80 (A=038, B=134) +Fout=1723.00 (A=039, B=134) +Fout=1723.20 (A=040, B=134) +Fout=1723.40 (A=041, B=134) +Fout=1723.60 (A=042, B=134) +Fout=1723.80 (A=043, B=134) +Fout=1724.00 (A=044, B=134) +Fout=1724.20 (A=045, B=134) +Fout=1724.40 (A=046, B=134) +Fout=1724.60 (A=047, B=134) +Fout=1724.80 (A=048, B=134) +Fout=1725.00 (A=049, B=134) +Fout=1725.20 (A=050, B=134) +Fout=1725.40 (A=051, B=134) +Fout=1725.60 (A=052, B=134) +Fout=1725.80 (A=053, B=134) +Fout=1726.00 (A=054, B=134) +Fout=1726.20 (A=055, B=134) +Fout=1726.40 (A=056, B=134) +Fout=1726.60 (A=057, B=134) +Fout=1726.80 (A=058, B=134) +Fout=1727.00 (A=059, B=134) +Fout=1727.20 (A=060, B=134) +Fout=1727.40 (A=061, B=134) +Fout=1727.60 (A=062, B=134) +Fout=1727.80 (A=063, B=134) +Fout=1728.00 (A=000, B=135) +Fout=1728.20 (A=001, B=135) +Fout=1728.40 (A=002, B=135) +Fout=1728.60 (A=003, B=135) +Fout=1728.80 (A=004, B=135) +Fout=1729.00 (A=005, B=135) +Fout=1729.20 (A=006, B=135) +Fout=1729.40 (A=007, B=135) +Fout=1729.60 (A=008, B=135) +Fout=1729.80 (A=009, B=135) +Fout=1730.00 (A=010, B=135) +Fout=1730.20 (A=011, B=135) +Fout=1730.40 (A=012, B=135) +Fout=1730.60 (A=013, B=135) +Fout=1730.80 (A=014, B=135) +Fout=1731.00 (A=015, B=135) +Fout=1731.20 (A=016, B=135) +Fout=1731.40 (A=017, B=135) +Fout=1731.60 (A=018, B=135) +Fout=1731.80 (A=019, B=135) +Fout=1732.00 (A=020, B=135) +Fout=1732.20 (A=021, B=135) +Fout=1732.40 (A=022, B=135) +Fout=1732.60 (A=023, B=135) +Fout=1732.80 (A=024, B=135) +Fout=1733.00 (A=025, B=135) +Fout=1733.20 (A=026, B=135) +Fout=1733.40 (A=027, B=135) +Fout=1733.60 (A=028, B=135) +Fout=1733.80 (A=029, B=135) +Fout=1734.00 (A=030, B=135) +Fout=1734.20 (A=031, B=135) +Fout=1734.40 (A=032, B=135) +Fout=1734.60 (A=033, B=135) +Fout=1734.80 (A=034, B=135) +Fout=1735.00 (A=035, B=135) +Fout=1735.20 (A=036, B=135) +Fout=1735.40 (A=037, B=135) +Fout=1735.60 (A=038, B=135) +Fout=1735.80 (A=039, B=135) +Fout=1736.00 (A=040, B=135) +Fout=1736.20 (A=041, B=135) +Fout=1736.40 (A=042, B=135) +Fout=1736.60 (A=043, B=135) +Fout=1736.80 (A=044, B=135) +Fout=1737.00 (A=045, B=135) +Fout=1737.20 (A=046, B=135) +Fout=1737.40 (A=047, B=135) +Fout=1737.60 (A=048, B=135) +Fout=1737.80 (A=049, B=135) +Fout=1738.00 (A=050, B=135) +Fout=1738.20 (A=051, B=135) +Fout=1738.40 (A=052, B=135) +Fout=1738.60 (A=053, B=135) +Fout=1738.80 (A=054, B=135) +Fout=1739.00 (A=055, B=135) +Fout=1739.20 (A=056, B=135) +Fout=1739.40 (A=057, B=135) +Fout=1739.60 (A=058, B=135) +Fout=1739.80 (A=059, B=135) +Fout=1740.00 (A=060, B=135) +Fout=1740.20 (A=061, B=135) +Fout=1740.40 (A=062, B=135) +Fout=1740.60 (A=063, B=135) +Fout=1740.80 (A=000, B=136) +Fout=1741.00 (A=001, B=136) +Fout=1741.20 (A=002, B=136) +Fout=1741.40 (A=003, B=136) +Fout=1741.60 (A=004, B=136) +Fout=1741.80 (A=005, B=136) +Fout=1742.00 (A=006, B=136) +Fout=1742.20 (A=007, B=136) +Fout=1742.40 (A=008, B=136) +Fout=1742.60 (A=009, B=136) +Fout=1742.80 (A=010, B=136) +Fout=1743.00 (A=011, B=136) +Fout=1743.20 (A=012, B=136) +Fout=1743.40 (A=013, B=136) +Fout=1743.60 (A=014, B=136) +Fout=1743.80 (A=015, B=136) +Fout=1744.00 (A=016, B=136) +Fout=1744.20 (A=017, B=136) +Fout=1744.40 (A=018, B=136) +Fout=1744.60 (A=019, B=136) +Fout=1744.80 (A=020, B=136) +Fout=1745.00 (A=021, B=136) +Fout=1745.20 (A=022, B=136) +Fout=1745.40 (A=023, B=136) +Fout=1745.60 (A=024, B=136) +Fout=1745.80 (A=025, B=136) +Fout=1746.00 (A=026, B=136) +Fout=1746.20 (A=027, B=136) +Fout=1746.40 (A=028, B=136) +Fout=1746.60 (A=029, B=136) +Fout=1746.80 (A=030, B=136) +Fout=1747.00 (A=031, B=136) +Fout=1747.20 (A=032, B=136) +Fout=1747.40 (A=033, B=136) +Fout=1747.60 (A=034, B=136) +Fout=1747.80 (A=035, B=136) +Fout=1748.00 (A=036, B=136) +Fout=1748.20 (A=037, B=136) +Fout=1748.40 (A=038, B=136) +Fout=1748.60 (A=039, B=136) +Fout=1748.80 (A=040, B=136) +Fout=1749.00 (A=041, B=136) +Fout=1749.20 (A=042, B=136) +Fout=1749.40 (A=043, B=136) +Fout=1749.60 (A=044, B=136) +Fout=1749.80 (A=045, B=136) +Fout=1750.00 (A=046, B=136) +Fout=1750.20 (A=047, B=136) +Fout=1750.40 (A=048, B=136) +Fout=1750.60 (A=049, B=136) +Fout=1750.80 (A=050, B=136) +Fout=1751.00 (A=051, B=136) +Fout=1751.20 (A=052, B=136) +Fout=1751.40 (A=053, B=136) +Fout=1751.60 (A=054, B=136) +Fout=1751.80 (A=055, B=136) +Fout=1752.00 (A=056, B=136) +Fout=1752.20 (A=057, B=136) +Fout=1752.40 (A=058, B=136) +Fout=1752.60 (A=059, B=136) +Fout=1752.80 (A=060, B=136) +Fout=1753.00 (A=061, B=136) +Fout=1753.20 (A=062, B=136) +Fout=1753.40 (A=063, B=136) +Fout=1753.60 (A=000, B=137) +Fout=1753.80 (A=001, B=137) +Fout=1754.00 (A=002, B=137) +Fout=1754.20 (A=003, B=137) +Fout=1754.40 (A=004, B=137) +Fout=1754.60 (A=005, B=137) +Fout=1754.80 (A=006, B=137) +Fout=1755.00 (A=007, B=137) +Fout=1755.20 (A=008, B=137) +Fout=1755.40 (A=009, B=137) +Fout=1755.60 (A=010, B=137) +Fout=1755.80 (A=011, B=137) +Fout=1756.00 (A=012, B=137) +Fout=1756.20 (A=013, B=137) +Fout=1756.40 (A=014, B=137) +Fout=1756.60 (A=015, B=137) +Fout=1756.80 (A=016, B=137) +Fout=1757.00 (A=017, B=137) +Fout=1757.20 (A=018, B=137) +Fout=1757.40 (A=019, B=137) +Fout=1757.60 (A=020, B=137) +Fout=1757.80 (A=021, B=137) +Fout=1758.00 (A=022, B=137) +Fout=1758.20 (A=023, B=137) +Fout=1758.40 (A=024, B=137) +Fout=1758.60 (A=025, B=137) +Fout=1758.80 (A=026, B=137) +Fout=1759.00 (A=027, B=137) +Fout=1759.20 (A=028, B=137) +Fout=1759.40 (A=029, B=137) +Fout=1759.60 (A=030, B=137) +Fout=1759.80 (A=031, B=137) +Fout=1760.00 (A=032, B=137) +Fout=1760.20 (A=033, B=137) +Fout=1760.40 (A=034, B=137) +Fout=1760.60 (A=035, B=137) +Fout=1760.80 (A=036, B=137) +Fout=1761.00 (A=037, B=137) +Fout=1761.20 (A=038, B=137) +Fout=1761.40 (A=039, B=137) +Fout=1761.60 (A=040, B=137) +Fout=1761.80 (A=041, B=137) +Fout=1762.00 (A=042, B=137) +Fout=1762.20 (A=043, B=137) +Fout=1762.40 (A=044, B=137) +Fout=1762.60 (A=045, B=137) +Fout=1762.80 (A=046, B=137) +Fout=1763.00 (A=047, B=137) +Fout=1763.20 (A=048, B=137) +Fout=1763.40 (A=049, B=137) +Fout=1763.60 (A=050, B=137) +Fout=1763.80 (A=051, B=137) +Fout=1764.00 (A=052, B=137) +Fout=1764.20 (A=053, B=137) +Fout=1764.40 (A=054, B=137) +Fout=1764.60 (A=055, B=137) +Fout=1764.80 (A=056, B=137) +Fout=1765.00 (A=057, B=137) +Fout=1765.20 (A=058, B=137) +Fout=1765.40 (A=059, B=137) +Fout=1765.60 (A=060, B=137) +Fout=1765.80 (A=061, B=137) +Fout=1766.00 (A=062, B=137) +Fout=1766.20 (A=063, B=137) +Fout=1766.40 (A=000, B=138) +Fout=1766.60 (A=001, B=138) +Fout=1766.80 (A=002, B=138) +Fout=1767.00 (A=003, B=138) +Fout=1767.20 (A=004, B=138) +Fout=1767.40 (A=005, B=138) +Fout=1767.60 (A=006, B=138) +Fout=1767.80 (A=007, B=138) +Fout=1768.00 (A=008, B=138) +Fout=1768.20 (A=009, B=138) +Fout=1768.40 (A=010, B=138) +Fout=1768.60 (A=011, B=138) +Fout=1768.80 (A=012, B=138) +Fout=1769.00 (A=013, B=138) +Fout=1769.20 (A=014, B=138) +Fout=1769.40 (A=015, B=138) +Fout=1769.60 (A=016, B=138) +Fout=1769.80 (A=017, B=138) +Fout=1770.00 (A=018, B=138) +Fout=1770.20 (A=019, B=138) +Fout=1770.40 (A=020, B=138) +Fout=1770.60 (A=021, B=138) +Fout=1770.80 (A=022, B=138) +Fout=1771.00 (A=023, B=138) +Fout=1771.20 (A=024, B=138) +Fout=1771.40 (A=025, B=138) +Fout=1771.60 (A=026, B=138) +Fout=1771.80 (A=027, B=138) +Fout=1772.00 (A=028, B=138) +Fout=1772.20 (A=029, B=138) +Fout=1772.40 (A=030, B=138) +Fout=1772.60 (A=031, B=138) +Fout=1772.80 (A=032, B=138) +Fout=1773.00 (A=033, B=138) +Fout=1773.20 (A=034, B=138) +Fout=1773.40 (A=035, B=138) +Fout=1773.60 (A=036, B=138) +Fout=1773.80 (A=037, B=138) +Fout=1774.00 (A=038, B=138) +Fout=1774.20 (A=039, B=138) +Fout=1774.40 (A=040, B=138) +Fout=1774.60 (A=041, B=138) +Fout=1774.80 (A=042, B=138) +Fout=1775.00 (A=043, B=138) +Fout=1775.20 (A=044, B=138) +Fout=1775.40 (A=045, B=138) +Fout=1775.60 (A=046, B=138) +Fout=1775.80 (A=047, B=138) +Fout=1776.00 (A=048, B=138) +Fout=1776.20 (A=049, B=138) +Fout=1776.40 (A=050, B=138) +Fout=1776.60 (A=051, B=138) +Fout=1776.80 (A=052, B=138) +Fout=1777.00 (A=053, B=138) +Fout=1777.20 (A=054, B=138) +Fout=1777.40 (A=055, B=138) +Fout=1777.60 (A=056, B=138) +Fout=1777.80 (A=057, B=138) +Fout=1778.00 (A=058, B=138) +Fout=1778.20 (A=059, B=138) +Fout=1778.40 (A=060, B=138) +Fout=1778.60 (A=061, B=138) +Fout=1778.80 (A=062, B=138) +Fout=1779.00 (A=063, B=138) +Fout=1779.20 (A=000, B=139) +Fout=1779.40 (A=001, B=139) +Fout=1779.60 (A=002, B=139) +Fout=1779.80 (A=003, B=139) +Fout=1780.00 (A=004, B=139) +Fout=1780.20 (A=005, B=139) +Fout=1780.40 (A=006, B=139) +Fout=1780.60 (A=007, B=139) +Fout=1780.80 (A=008, B=139) +Fout=1781.00 (A=009, B=139) +Fout=1781.20 (A=010, B=139) +Fout=1781.40 (A=011, B=139) +Fout=1781.60 (A=012, B=139) +Fout=1781.80 (A=013, B=139) +Fout=1782.00 (A=014, B=139) +Fout=1782.20 (A=015, B=139) +Fout=1782.40 (A=016, B=139) +Fout=1782.60 (A=017, B=139) +Fout=1782.80 (A=018, B=139) +Fout=1783.00 (A=019, B=139) +Fout=1783.20 (A=020, B=139) +Fout=1783.40 (A=021, B=139) +Fout=1783.60 (A=022, B=139) +Fout=1783.80 (A=023, B=139) +Fout=1784.00 (A=024, B=139) +Fout=1784.20 (A=025, B=139) +Fout=1784.40 (A=026, B=139) +Fout=1784.60 (A=027, B=139) +Fout=1784.80 (A=028, B=139) +Fout=1785.00 (A=029, B=139) +Fout=1785.20 (A=030, B=139) +Fout=1785.40 (A=031, B=139) +Fout=1785.60 (A=032, B=139) +Fout=1785.80 (A=033, B=139) +Fout=1786.00 (A=034, B=139) +Fout=1786.20 (A=035, B=139) +Fout=1786.40 (A=036, B=139) +Fout=1786.60 (A=037, B=139) +Fout=1786.80 (A=038, B=139) +Fout=1787.00 (A=039, B=139) +Fout=1787.20 (A=040, B=139) +Fout=1787.40 (A=041, B=139) +Fout=1787.60 (A=042, B=139) +Fout=1787.80 (A=043, B=139) +Fout=1788.00 (A=044, B=139) +Fout=1788.20 (A=045, B=139) +Fout=1788.40 (A=046, B=139) +Fout=1788.60 (A=047, B=139) +Fout=1788.80 (A=048, B=139) +Fout=1789.00 (A=049, B=139) +Fout=1789.20 (A=050, B=139) +Fout=1789.40 (A=051, B=139) +Fout=1789.60 (A=052, B=139) +Fout=1789.80 (A=053, B=139) +Fout=1790.00 (A=054, B=139) +Fout=1790.20 (A=055, B=139) +Fout=1790.40 (A=056, B=139) +Fout=1790.60 (A=057, B=139) +Fout=1790.80 (A=058, B=139) +Fout=1791.00 (A=059, B=139) +Fout=1791.20 (A=060, B=139) +Fout=1791.40 (A=061, B=139) +Fout=1791.60 (A=062, B=139) +Fout=1791.80 (A=063, B=139) +Fout=1792.00 (A=000, B=140) +Fout=1792.20 (A=001, B=140) +Fout=1792.40 (A=002, B=140) +Fout=1792.60 (A=003, B=140) +Fout=1792.80 (A=004, B=140) +Fout=1793.00 (A=005, B=140) +Fout=1793.20 (A=006, B=140) +Fout=1793.40 (A=007, B=140) +Fout=1793.60 (A=008, B=140) +Fout=1793.80 (A=009, B=140) +Fout=1794.00 (A=010, B=140) +Fout=1794.20 (A=011, B=140) +Fout=1794.40 (A=012, B=140) +Fout=1794.60 (A=013, B=140) +Fout=1794.80 (A=014, B=140) +Fout=1795.00 (A=015, B=140) +Fout=1795.20 (A=016, B=140) +Fout=1795.40 (A=017, B=140) +Fout=1795.60 (A=018, B=140) +Fout=1795.80 (A=019, B=140) +Fout=1796.00 (A=020, B=140) +Fout=1796.20 (A=021, B=140) +Fout=1796.40 (A=022, B=140) +Fout=1796.60 (A=023, B=140) +Fout=1796.80 (A=024, B=140) +Fout=1797.00 (A=025, B=140) +Fout=1797.20 (A=026, B=140) +Fout=1797.40 (A=027, B=140) +Fout=1797.60 (A=028, B=140) +Fout=1797.80 (A=029, B=140) +Fout=1798.00 (A=030, B=140) +Fout=1798.20 (A=031, B=140) +Fout=1798.40 (A=032, B=140) +Fout=1798.60 (A=033, B=140) +Fout=1798.80 (A=034, B=140) +Fout=1799.00 (A=035, B=140) +Fout=1799.20 (A=036, B=140) +Fout=1799.40 (A=037, B=140) +Fout=1799.60 (A=038, B=140) +Fout=1799.80 (A=039, B=140) +Fout=1800.00 (A=040, B=140) +Fout=1800.20 (A=041, B=140) +Fout=1800.40 (A=042, B=140) +Fout=1800.60 (A=043, B=140) +Fout=1800.80 (A=044, B=140) +Fout=1801.00 (A=045, B=140) +Fout=1801.20 (A=046, B=140) +Fout=1801.40 (A=047, B=140) +Fout=1801.60 (A=048, B=140) +Fout=1801.80 (A=049, B=140) +Fout=1802.00 (A=050, B=140) +Fout=1802.20 (A=051, B=140) +Fout=1802.40 (A=052, B=140) +Fout=1802.60 (A=053, B=140) +Fout=1802.80 (A=054, B=140) +Fout=1803.00 (A=055, B=140) +Fout=1803.20 (A=056, B=140) +Fout=1803.40 (A=057, B=140) +Fout=1803.60 (A=058, B=140) +Fout=1803.80 (A=059, B=140) +Fout=1804.00 (A=060, B=140) +Fout=1804.20 (A=061, B=140) +Fout=1804.40 (A=062, B=140) +Fout=1804.60 (A=063, B=140) +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.40 (A=063, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.20 (A=063, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.00 (A=063, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1855.80 (A=063, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.60 (A=063, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.40 (A=063, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.20 (A=063, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.00 (A=063, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1919.80 (A=063, B=149) diff --git a/src/host/rita_pll/rita_pll_notes.txt b/src/host/rita_pll/rita_pll_notes.txt new file mode 100644 index 000000000..8557d3ae9 --- /dev/null +++ b/src/host/rita_pll/rita_pll_notes.txt @@ -0,0 +1,8 @@ + +Regular Operation as per DS GSM SPEC + +GSM900 Tx: 870.4 ... 921.4 MHz 880.0 ... 914.8 +GSM900 Rx: 864.4 ... 966.2 MHz 925.0 ... 959.8 + +GSM1800 Tx: 1702.4 ... 1919.8 MHz 1710.2 ... 1784.8 +GSM1800 Rx: 1804.8 ... 1996.4 MHz 1805.2 ... 1879.8 diff --git a/src/target/firmware/.gitignore b/src/target/firmware/.gitignore new file mode 100644 index 000000000..5dff144b0 --- /dev/null +++ b/src/target/firmware/.gitignore @@ -0,0 +1,7 @@ +*.o +*.a +*.lst +*.bin +*.elf +*.size +*~ diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile new file mode 100644 index 000000000..0e5ff2ccd --- /dev/null +++ b/src/target/firmware/Makefile @@ -0,0 +1,69 @@ +INCLUDES=-Iinclude/ -I../../../include +-include Makefile.inc + +# individual list of object files, they should probably become libraries +FLASH_OBJS=flash/cfi_flash.o +DISPLAY_OBJS=display/font_r8x8.o display/st7558.o +ABB_OBJS=abb/twl3025.o +RF_OBJS=rf/trf6151.o +BOARD_C123_OBJS=board/common/rffe_compal_dualband.o board/compal_e88/init.o +START=board/common/compal_ramload_start.S +LDS=board/common/compal_ramload.lds + +# The objects that we want to link with every application +OBJS=start.o $(ABB_OBJS) $(RF_OBJS) $(DISPLAY_OBJS) $(FLASH_OBJS) $(BOARD_C123_OBJS) + +# The libraries that we want to link with every application +LIBS=calypso/libcalypso.a layer1/liblayer1.a lib/libmini.a comm/libcomm.a + +# The list of applications we ant to build. Please add your apps here! +APPS=hello_world l1test compal_dump compal_dsp_dump layer1 + +APP_BINS=$(APPS:=.bin) +APP_ELFS=$(APPS:=.elf) +APP_OBJS=$(patsubst %,apps/%/main.o, $(APPS)) +APP_SIZES=$(APP_ELFS:.elf=.size) + +LST=$(OBJS:.o=.lst) $(APP_OBJS:.o=.lst) $(START:.S=.lst) + +all: $(APP_BINS) $(APP_ELFS) $(APP_SIZES) + +start.o: $(START) + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +%.elf: $(OBJS) apps/%/main.o $(LIBS) + $(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $(LDS) -Bstatic -Map $@.map -o $@ --start-group $^ --end-group + $(CROSS_COMPILE)$(SIZE) $@ + +%.size: %.elf + $(CROSS_COMPILE)$(SIZE) -A $^ > $@ + +%.bin: %.elf + $(CROSS_COMPILE)objcopy --gap-fill=0xff -O binary $^ $@ + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: calypso/libcalypso.a +calypso/libcalypso.a: + make -C calypso all + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: layer1/liblayer1.a +layer1/liblayer1.a: + make -C layer1 all + +lib/libmini.a: + make -C lib all + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: comm/libcomm.a +comm/libcomm.a: + make -C comm all +clean: + make -C calypso clean + make -C layer1 clean + make -C lib clean + make -C comm clean + rm -f *.map $(OBJS) $(APP_BINS) $(APP_ELFS) $(APP_SIZES) $(LST) diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc new file mode 100644 index 000000000..f3f194788 --- /dev/null +++ b/src/target/firmware/Makefile.inc @@ -0,0 +1,19 @@ +CROSS_COMPILE?=arm-elf- +CC=gcc +LD=ld +SIZE=size +OBJCOPY=objcopy + +DEBUGF=dwarf-2 + +CFLAGS=-mcpu=arm7tdmi $(INCLUDES) +CFLAGS += -Wall -Wextra -Wcast-align -Wimplicit -Wunused +CFLAGS += -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wnested-externs +CFLAGS += -Wbad-function-cast -Wsign-compare -Waggregate-return +CFLAGS += -Wa,-adhlns=$(subst $(suffix $<),.lst,$<) +CFLAGS += -Os -ffunction-sections +CFLAGS += -g$(DEBUGF) + +ASFLAGS=-Wa,-adhlns=$(<:.S=.lst),--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__ + +LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections #-Wl,-Map=$(TARGET).map,--cref diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c new file mode 100644 index 000000000..bce6cc6d7 --- /dev/null +++ b/src/target/firmware/abb/twl3025.c @@ -0,0 +1,291 @@ +/* Driver for Analog Baseband Circuit (TWL3025) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* TWL3025 */ +#define REG_PAGE(n) (n >> 7) +#define REG_ADDR(n) (n & 0x3f) + +#define TWL3025_DEV_IDX 0 /* On the SPI bus */ +#define TWL3025_TSP_DEV_IDX 0 /* On the TSP bus */ + +struct twl3025 { + uint8_t page; +}; +static struct twl3025 twl3025_state; + +/* Switch the register page of the TWL3025 */ +static void twl3025_switch_page(uint8_t page) +{ + if (page == 0) + twl3025_reg_write(PAGEREG, 1 << 0); + else + twl3025_reg_write(PAGEREG, 1 << 1); + + twl3025_state.page = page; +} + +static void handle_charger(void) +{ + uint16_t status; + printd("handle_charger();"); + + status = twl3025_reg_read(VRPCSTS); +// printd("\nvrpcsts: 0x%02x", status); + + if (status & 0x40) { + printd(" inserted\n"); + } else { + printd(" removed\n"); + } + +// twl3025_dump_madc(); +} + +static void handle_adc_done(void) +{ + printd("handle_adc_done();"); +} + +static void twl3025_irq(enum irq_nr nr) +{ + uint16_t src; + printd("twl3025_irq: 0x%02x\n",nr); + switch (nr){ + case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done + src = twl3025_reg_read(ITSTATREG); +// printd("itstatreg 0x%02x\n", src); + if (src & 0x08) + handle_charger(); + if (src & 0x20) + handle_adc_done(); + break; + case IRQ_EXTERNAL_FIQ: // vcc <2.8V emergency power off + puts("\nBROWNOUT!1!"); + twl3025_power_off(); + break; + default: + return; + } +} + +void twl3025_init(void) +{ + spi_init(); + twl3025_switch_page(0); + twl3025_clk13m(1); + twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */ + twl3025_unit_enable(TWL3025_UNIT_AFC, 1); + + irq_register_handler(IRQ_EXTERNAL, &twl3025_irq); + irq_config(IRQ_EXTERNAL, 0, 0, 0); + irq_enable(IRQ_EXTERNAL); + + irq_register_handler(IRQ_EXTERNAL_FIQ, &twl3025_irq); + irq_config(IRQ_EXTERNAL_FIQ, 1, 0, 0); + irq_enable(IRQ_EXTERNAL_FIQ); +} + +void twl3025_reg_write(uint8_t reg, uint16_t data) +{ + uint16_t tx; + + printd("tw3025_reg_write(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), data); + + if (reg != PAGEREG && REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1); + + spi_xfer(TWL3025_DEV_IDX, 16, &tx, NULL); +} + +void twl3025_tsp_write(uint8_t data) +{ + tsp_write(TWL3025_TSP_DEV_IDX, 7, data); +} + +uint16_t twl3025_reg_read(uint8_t reg) +{ + uint16_t tx, rx; + + if (REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = (REG_ADDR(reg) << 1) | 1; + + /* A read cycle contains two SPI transfers */ + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + delay_ms(1); + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + + rx >>= 6; + + printd("tw3025_reg_read(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), rx); + + return rx; +} + +static void twl3025_wait_ibic_access(void) +{ + /* Wait 6 * 32kHz clock cycles for first IBIC access (187us + 10% = 210us) */ + delay_ms(1); +} + +void twl3025_power_off(void) +{ + twl3025_reg_write(VRPCDEV, 0x01); +} + +void twl3025_clk13m(int enable) +{ + if (enable) { + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + /* for whatever reason we need to do this twice */ + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + } else { + twl3025_reg_write(TOGBR2, TOGBR2_ACTR); + twl3025_wait_ibic_access(); + } +} + +#define TSP_DELAY 6 /* 13* Tclk6M5 = ~ 3 GSM Qbits + 3 TPU instructions */ +#define BDLON_TO_BDLCAL 6 +#define BDLCAL_DURATION 66 +#define BDLON_TO_BDLENA 7 +#define BULON_TO_BULENA 16 + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at) +{ + int16_t bdl_ena = at - TSP_DELAY - 6; + + if (on) { + if (bdl_ena < 0) + printf("BDLENA time negative (%d)\n", bdl_ena); + /* FIXME: calibration should be done just before BDLENA */ + twl3025_tsp_write(BDLON); + tpu_enq_wait(BDLON_TO_BDLCAL - TSP_DELAY); + twl3025_tsp_write(BDLON | BDLCAL); + tpu_enq_wait(BDLCAL_DURATION - TSP_DELAY); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(BDLCAL_TO_BDLENA) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON | BDLENA); + } else { + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(nBDLENA_TO_nBDLON) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + twl3025_tsp_write(0); + } +} + +void twl3025_afc_set(int16_t val) +{ + printf("twl3025_afc_set(%d)\n", val); + + if (val > 4095) + val = 4095; + else if (val <= -4096) + val = -4096; + + /* FIXME: we currently write from the USP rather than BSP */ + twl3025_reg_write(AUXAFC2, val >> 10); + twl3025_reg_write(AUXAFC1, val & 0x3ff); +} + +int16_t twl3025_afc_get(void) +{ + int16_t val; + + val = (twl3025_reg_read(AUXAFC2) & 0x7); + val = val << 10; + val = val | (twl3025_reg_read(AUXAFC1) & 0x3ff); + + if (val > 4095) + val = -(8192 - val); + return val; +} + +void twl3025_unit_enable(enum twl3025_unit unit, int on) +{ + uint16_t togbr1 = 0; + + switch (unit) { + case TWL3025_UNIT_AFC: + if (on) + togbr1 = (1 << 7); + else + togbr1 = (1 << 6); + break; + case TWL3025_UNIT_MAD: + if (on) + togbr1 = (1 << 9); + else + togbr1 = (1 << 8); + break; + case TWL3025_UNIT_ADA: + if (on) + togbr1 = (1 << 5); + else + togbr1 = (1 << 4); + case TWL3025_UNIT_VDL: + if (on) + togbr1 = (1 << 3); + else + togbr1 = (1 << 2); + break; + case TWL3025_UNIT_VUL: + if (on) + togbr1 = (1 << 1); + else + togbr1 = (1 << 0); + break; + } + twl3025_reg_write(TOGBR1, togbr1); +} + +uint8_t twl3025_afcout_get(void) +{ + return twl3025_reg_read(AFCOUT) & 0xff; +} + +void twl3025_afcout_set(uint8_t val) +{ + twl3025_reg_write(AFCCTLADD, 0x05); + twl3025_reg_write(AFCOUT, val); +} 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 000000000..468db2355 --- /dev/null +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -0,0 +1,78 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 Harald Welte + * (C) 2010 Sylvain Munaut + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void main(void) +{ + int i; + uint16_t twl_reg; + + puts("\n\nCompal DSP data dumper\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Initialize basic board support */ + board_init(); + puts(hr); + + /* Dump DSP content */ + dsp_dump(); + + while (1) {} +} + diff --git a/src/target/firmware/apps/compal_dump/main.c b/src/target/firmware/apps/compal_dump/main.c new file mode 100644 index 000000000..444dfd47b --- /dev/null +++ b/src/target/firmware/apps/compal_dump/main.c @@ -0,0 +1,90 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* 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; } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + puts("\n\nCompal device data dumper\n"); + puts(hr); + + /* Disable watchdog (for phones that have it enabled after boot) */ + wdog_enable(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Initialize flash, dumping the protection area. */ + cfi_flash_t f; + flash_init(&f, 0x00000000); + flash_dump_info(&f); + puts(hr); + + /* Dump flash contents */ + printf("Dump %d kbytes of external flash\n", f.f_size/1024); + memdump_range((void *)0x00000000, f.f_size); + puts(hr); + + /* Power down */ + twl3025_power_off(); + + while (1) {} +} + 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 000000000..7f6aeef9a --- /dev/null +++ b/src/target/firmware/apps/hello_world/main.c @@ -0,0 +1,126 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; } +} + +/* 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); + st7558_puts(msg->data); + msgb_free(msg); +} + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\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 aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("Hello World"); + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + + /* 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; + } +} diff --git a/src/target/firmware/apps/l1test/main.c b/src/target/firmware/apps/l1test/main.c new file mode 100644 index 000000000..b19f72c1f --- /dev/null +++ b/src/target/firmware/apps/l1test/main.c @@ -0,0 +1,289 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define SCAN + +#ifdef SCAN +/* if scanning is enabled, scan from 0 ... 124 */ +#define BASE_ARFCN 0 +#else +/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */ +#define BASE_ARFCN 871 +#endif + +/* 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; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +/* Best ARFCN MAP ************************************************************/ + +struct arfcn_map { + uint16_t arfcn; + int16_t dbm8; +}; + +static struct arfcn_map best_arfcn_map[10]; +static void best_arfcn_update(uint16_t arfcn, int16_t dbm8) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 < dbm8 || + best_arfcn_map[i].dbm8 == 0) { + best_arfcn_map[i].dbm8 = dbm8; + best_arfcn_map[i].arfcn = arfcn; + return; + } + } +} + +static void best_arfcn_dump(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 == 0) + continue; + printf("ARFCN %3d: %d dBm\n", + best_arfcn_map[i].arfcn, + best_arfcn_map[i].dbm8/8); + } +} + + +/* MAIN program **************************************************************/ + +enum l1test_state { + STATE_NONE, + STATE_PM, + STATE_FB, +}; + +static enum l1test_state l1test_state; + +static void l1test_state_change(enum l1test_state new_state) +{ + switch (new_state) { + case STATE_PM: + puts("Performing power measurement over GSM900\n"); + l1s_pm_test(1, BASE_ARFCN); + break; + case STATE_FB: + puts("Starting FCCH Recognition\n"); + l1s_fb_test(1, 0); + break; + case STATE_NONE: + /* disable frame interrupts */ + tpu_frame_irq_en(0, 0); + break; + } +} + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + best_arfcn_update(sig->arfcn, sig->pm.dbm8[0]); + next_arfcn = sig->arfcn + 1; + + if (next_arfcn >= 124) { + puts("ARFCN Top 10 Rx Level\n"); + best_arfcn_dump(); + + trf6151_rx_window(0, best_arfcn_map[0].arfcn, 40, 0); + tpu_end_scenario(); + + /* PM phase completed, do FB det */ + l1test_state_change(STATE_FB); + + break; + } + + /* restart Power Measurement */ + l1s_pm_test(1, next_arfcn); + break; + case L1_SIG_NB: + puts("NB SNR "); + for (i = 0; i < 4; i++) { + uint16_t snr = sig->nb.meas[i].snr; + printf("%d.%03u ", l1s_snr_int(snr), l1s_snr_fract(snr)); + } + putchar('\n'); + printf("--> Frame %d %d 0x%04X ", sig->nb.fire, sig->nb.crc, sig->nb.num_biterr); + for (i = 0; i < ARRAY_SIZE(sig->nb.frame); i++) + printf("%02X ", sig->nb.frame[i]); + putchar('\n'); + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("l1test.bin"); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + //dsp_checksum_task(); +#ifdef SCAN + l1test_state_change(STATE_PM); +#else + l1test_state_change(STATE_FB); +#endif + tpu_frame_irq_en(1, 1); + + while (1) {} + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +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_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + 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; + } +} diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c new file mode 100644 index 000000000..3b1b686a9 --- /dev/null +++ b/src/target/firmware/apps/layer1/main.c @@ -0,0 +1,214 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* 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; } +} + +const char *hr = "======================================================================\n"; + +/* MAIN program **************************************************************/ + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + break; + case L1_SIG_NB: + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); +static void la1_l23_rx_cb(uint8_t dlci, struct msgb *msg); + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("layer1.bin"); + + sercomm_register_rx_cb(SC_DLCI_L1A_L23, la1_l23_rx_cb); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + tpu_frame_irq_en(1, 1); + + while (1) {} + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +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_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + 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; + } +} + +static void la1_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + struct l1_info_ul *ul = msg->data; + struct l1_sync_new_ccch_req *sync_req; + + if (sizeof(*ul) > msg->len) { + printf("la1_l23_cb: Short message. %u\n", msg->len); + goto exit; + } + + switch (ul->msg_type) { + case SYNC_NEW_CCCH_REQ: + if (sizeof(*ul) + sizeof(*sync_req) > msg->len) { + printf("Short sync msg. %u\n", msg->len); + break; + } + + sync_req = (struct l1_sync_new_ccch_req *) (&msg->data[0] + sizeof(*ul)); + printf("Asked to tune to frequency: %u\n", sync_req->band_arfcn); + break; + case DEDIC_MODE_EST_REQ: + break; + } + +exit: + msgb_free(msg); +} diff --git a/src/target/firmware/board/common/compal_ramload.lds b/src/target/firmware/board/common/compal_ramload.lds new file mode 100644 index 000000000..ae791c5d5 --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload.lds @@ -0,0 +1,83 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* area that can be initialized by the loader (plus some reserved stuff) */ + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + /* remainder of internal ram, can be used for bss and the like */ + IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00030000 + /* external ram on a C123 */ + ERAM (rw) : ORIGIN = 0x01000000, LENGTH = 0x00040000 +} +SECTIONS +{ + . = 0x800000; + + /* reserved (what is in here?) */ + .compal.reserved1 (NOLOAD) : { . = 0x100; } > LRAM + + /* XXX: leftovers from exception vector trickery development? */ + /* .compal.reserved1 (NOLOAD) : { . = 0x1C; } > LRAM */ + /* .compal.reserved2 (NOLOAD) : { . = 0xC8; } > LRAM */ + + /* image signature (prepended by compal_dnload according to phone type) */ + .compal.header (NOLOAD) : { . = 4; } > LRAM + + /* code */ + . = ALIGN(4); + .text_start : { + /* initialization code */ + PROVIDE(_start = .); + KEEP(*(.init)) + *(.text._start) + _exceptions = .; + } > LRAM + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c: AT (LOADADDR(.text_start) + SIZEOF(.text_start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > LRAM + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + } > LRAM + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata) + } > LRAM + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > LRAM + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + } > LRAM + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/common/compal_ramload_start.S b/src/target/firmware/board/common/compal_ramload_start.S new file mode 100644 index 000000000..4f6fadef9 --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload_start.S @@ -0,0 +1,257 @@ + +#define BA_UART_MODEM 0xFFFF5800 + +# + +.macro senduart, rd, rx + strb \rd, [\rx, #0] +.endm + +.macro busyuart, rd, rx +1001: + @busy waiting until THR is empty + ldrb \rd, [\rx, #5] @ read LSR register + mov \rd, \rd, lsr #6 + tst \rd, #1 + beq 1001b +.endm + +.macro loadsp, rd + ldr \rd, =BA_UART_MODEM +.endm + + + .EQU ARM_MODE_FIQ, 0x11 + .EQU ARM_MODE_IRQ, 0x12 + .EQU ARM_MODE_SVC, 0x13 + + .EQU I_BIT, 0x80 + .EQU F_BIT, 0x40 + +#define TOP_OF_RAM 0x083fff0 +#define FIQ_STACK_SIZE 1024 +#define IRQ_STACK_SIZE 1024 + +.section .text._start +.globl _start +_start: + @ clear bss section + .global __bss_start + .global __bss_end + mov r0, #0 + ldr r1, =__bss_start + ldr r2, =__bss_end +2: cmp r1, r2 + strlo r0, [r1], #4 + blo 2b + + ldr r0, =TOP_OF_RAM + /* initialize FIQ stack */ + msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #FIQ_STACK_SIZE + + /* initialize IRQ stack */ + msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #IRQ_STACK_SIZE + + /* initialize supervisor stack */ + msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT + mov r13, r0 + + @ set backlight to moderate level + bl pwl_init + mov r0, #50 + bl pwl_set_level + + @ldr r0, =string + @bl puts_asm + + @ some memory dumps + @ldr r0, =0xfffef000 + @bl memdump + @ldr r0, =0xfffffe00 + @bl memdump + + ldr pc, _jump_main + + @ endless loop at end of program +_end: b _end + b _start + +_jump_main: .word main + +string: .word 0x6c6c6548 + .word 0x6f57206f + .word 0x00646c72 + +foo: .word 0xee4c9f63 +bar: .word 0x639f4cee + + .align 2 + .type phexbuf, #object +phexbuf: .space 12 + .size phexubf, . - phexbuf + +.globl phex +phex: adr r3, phexbuf + mov r2, #0 + strb r2, [r3, r1] +1: subs r1, r1, #1 + movmi r0, r3 + bmi puts_asm + and r2, r0, #15 + mov r0, r0, lsr #4 + cmp r2, #10 + addge r2, r2, #7 + add r2, r2, #'0' + strb r2, [r3, r1] + b 1b + +puts_asm: loadsp r3 +1: ldrb r2, [r0], #1 + teq r2, #0 + moveq pc, lr +2: senduart r2, r3 + busyuart r1, r3 + teq r2, #'\n' + moveq r2, #'\r' + beq 2b + teq r0, #0 + bne 1b + mov pc, lr +.globl putchar_asm +putchar_asm: + mov r2, r0 + mov r0, #0 + loadsp r3 + b 2b + +memdump: mov r12, r0 + mov r10, lr + mov r11, #0 +2: mov r0, r11, lsl #2 + add r0, r0, r12 + mov r1, #8 + bl phex + mov r0, #':' + bl putchar_asm +1: mov r0, #' ' + bl putchar_asm + ldr r0, [r12, r11, lsl #2] + mov r1, #8 + bl phex + and r0, r11, #7 + teq r0, #3 + moveq r0, #' ' + bleq putchar_asm + and r0, r11, #7 + add r11, r11, #1 + teq r0, #7 + bne 1b + mov r0, #'\n' + bl putchar_asm + cmp r11, #64 + blt 2b + mov pc, r10 + + +#define ASIC_CONF_REG 0xfffef008 +#define BA_PWL 0xfffe8000 + +pwl_init: ldr r1, =ASIC_CONF_REG + ldr r2, [r1] + orr r2, r2, #0x10 @ set light output to PWL + str r2, [r1] + ldr r1, =BA_PWL + mov r0, #1 + strb r0, [r1, #1] @ enable clock of PWL unut + mov pc, lr + +pwl_set_level: ldr r1, =BA_PWL + strb r0, [r1] + mov pc, lr + +handle_abort: + @ print the PC we would jump back to... + sub lr, lr, #4 @ we assume to be ARM32 + + mov r0, lr + mov r1, #8 + bl phex + + @ print abort message + mov r0, #'A' + bl putchar_asm + mov r0, #'B' + bl putchar_asm + mov r0, #'O' + bl putchar_asm + mov r0, #'R' + bl putchar_asm + mov r0, #'T' + bl putchar_asm +0: @ dead + b 0b + +irq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl irq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +fiq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl fiq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +/* Exception Vectors like they are needed for the exception vector + indirection of the internal boot ROM. The following section must be liked + to appear at 0x80'001c */ +.section .text.exceptions +_undef_instr: + b handle_abort +_sw_interr: + b _sw_interr +_prefetch_abort: + b handle_abort +_data_abort: + b handle_abort +_reserved: + b _reserved +_irq: + b irq_entry +_fiq: + b fiq_entry diff --git a/src/target/firmware/board/common/rffe_compal_dualband.c b/src/target/firmware/board/common/rffe_compal_dualband.c new file mode 100644 index 000000000..b796c6f52 --- /dev/null +++ b/src/target/firmware/board/common/rffe_compal_dualband.c @@ -0,0 +1,70 @@ +#include +#include + +#include +#include +#include +#include +#include + +/* describe how the RF frontend is wired on the Motorola E88 board (C117/C118/C121/C123) */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(1) /* Enable the Power Amplifier */ +#define TRENA TSPACT(6) /* Transmit Enable (Antenna Switch) */ +#define GSM_TXEN TSPACT(8) /* GSM (as opposed to DCS) Transmit */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= TRENA | GSM_TXEN; /* low-active */ + + /* Then we selectively set the bits on, if required */ + if (tx) { + tspact &= ~TRENA; + if (band == GSM_900) + tspact &= ~GSM_TXEN; + } + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 5); /* TSPACT6 I/O function, not nCS6 */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 5); /* TSPACT8 I/O function, not nMREQ */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + +} diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c new file mode 100644 index 000000000..f51f3e8d9 --- /dev/null +++ b/src/target/firmware/board/compal_e88/init.c @@ -0,0 +1,126 @@ +/* Initialization for the Compal E88 (Motorola C115...C123) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +void board_init(void) +{ + /* FIXME: this needs to go to board_e99/init.c once we have it */ + wdog_enable(0); + + static cfi_flash_t flash; + // XXX: move after mapping initialization and use final address + flash_init(&flash, 0x00000000); + + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR); + uart_baudrate(CONS_UART_NR, UART_115200); + + hwtimer_init(); + + dma_init(); + rtc_init(); + + /* Initialize LCD driver (uses I2C) */ + st7558_init(); + + keypad_init(); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile new file mode 100644 index 000000000..a2be3cfa1 --- /dev/null +++ b/src/target/firmware/calypso/Makefile @@ -0,0 +1,17 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +OBJS=arm.o clock.o dma.o dsp.o du.o i2c.o irq.o rtc.o spi.o tpu.o tsp.o keypad.o misc.o timer.o backlight.o uart.o + +LST=$(OBJS:.o=.lst) + +all: libcalypso.a + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +libcalypso.a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/calypso/arm.c b/src/target/firmware/calypso/arm.c new file mode 100644 index 000000000..8794ee35f --- /dev/null +++ b/src/target/firmware/calypso/arm.c @@ -0,0 +1,26 @@ + +/* enable IRQ+FIQ interrupts */ +void arm_enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0xc0\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + +/* disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them */ +int arm_disable_interrupts(void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c new file mode 100644 index 000000000..e2ff29ccb --- /dev/null +++ b/src/target/firmware/calypso/backlight.c @@ -0,0 +1,67 @@ +/* Calypso DBB internal PWL (Pulse Width / Light) Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#define BASE_ADDR_PWL 0xfffe8000 +#define PWL_REG(m) (BASE_ADDR_PWL + (m)) + +#define ASIC_CONF_REG 0xfffef008 +#define LIGHT_LEVEL_REG 0xfffe4810 + +enum pwl_reg { + PWL_LEVEL = 0, + PWL_CTRL = 2, +}; + +#define ASCONF_PWL_ENA (1 << 4) + +void bl_mode_pwl(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + writeb(0x01, PWL_REG(PWL_CTRL)); + /* Switch pin from LT to PWL */ + reg |= ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + } else { + /* Switch pin from PWL to LT */ + reg |= ~ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + writeb(0x00, PWL_REG(PWL_CTRL)); + } +} + +void bl_level(uint8_t level) +{ + if (readw(ASIC_CONF_REG) & ASCONF_PWL_ENA) { + writeb(level, PWL_REG(PWL_LEVEL)); + } else { + /* we need to scale the light level, as the + * ARMIO light controller only knows 0..63 */ + writeb(level>>2, LIGHT_LEVEL_REG); + } +} diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c new file mode 100644 index 000000000..d5b2c0906 --- /dev/null +++ b/src/target/firmware/calypso/clock.c @@ -0,0 +1,202 @@ +/* Driver for Calypso clock management */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +//#define DEBUG +#include + +#include +#include + +#define REG_DPLL 0xffff9800 +#define DPLL_LOCK (1 << 0) +#define DPLL_BREAKLN (1 << 1) +#define DPLL_BYPASS_DIV_SHIFT 2 /* 2 bits */ +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_PLL_DIV_SHIFT 5 /* 2 bits */ +#define DPLL_PLL_MULT_SHIFT 7 /* 5 bits */ +#define DPLL_TEST (1 << 12) +#define DPLL_IOB (1 << 13) /* Initialize on break */ +#define DPLL_IAI (1 << 14) /* Initialize after Idle */ + +#define BASE_ADDR_CLKM 0xfffffd00 +#define CLKM_REG(m) (BASE_ADDR_CLKM+(m)) + +enum clkm_reg { + CNTL_ARM_CLK = 0, + CNTL_CLK = 2, + CNTL_RST = 4, + CNTL_ARM_DIV = 8, +}; + +/* CNTL_ARM_CLK */ +#define ARM_CLK_BIG_SLEEP (1 << 0) /* MCU Master Clock enabled? */ +#define ARM_CLK_CLKIN_SEL0 (1 << 1) /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */ +#define ARM_CLK_CLKIN_SEL (1 << 2) /* 0 = VTCXO or 1 = CLKIN */ +#define ARM_CLK_MCLK_DIV5 (1 << 3) /* enable 1.5 or 2.5 division factor */ +#define ARM_CLK_MCLK_DIV_SHIFT 4 /* 3 bits */ +#define ARM_CLK_DEEP_POWER_SHIFT 8 +#define ARM_CLK_DEEP_SLEEP 12 + +/* CNTL_CLK */ +#define CLK_IRQ_CLK_DIS (1 << 0) /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */ +#define CLK_BRIDGE_CLK_DIS (1 << 1) +#define CLK_TIMER_CLK_DIS (1 << 2) +#define CLK_DPLL_DIS (1 << 3) /* 0: DPLL is not stopped during SLEEP */ +#define CLK_CLKOUT_EN (1 << 4) /* Enable CLKOUT output pins */ +#define CLK_EN_IDLE3_FLG (1 << 5) /* DSP idle flag control (1 = + * SAM/HOM register forced to HOM when DSP IDLE3) */ +#define CLK_VCLKOUT_DIV2 (1 << 6) /* 1: VCLKOUT-FR is divided by 2 */ +#define CLK_VTCXO_DIV2 (1 << 7) /* 1: VTCXO is dividied by 2 */ + +#define BASE_ADDR_MEMIF 0xfffffb00 +#define MEMIF_REG(x) (BASE_ADDR_MEMIF+(x)) + +enum memif_reg { + API_RHEA_CTL = 0x0e, + EXTRA_CONF = 0x10, +}; + +static void dump_reg16(uint32_t addr, char *name) +{ + printf("%s=0x%04x\n", name, readw(addr)); +} + +void calypso_clk_dump(void) +{ + dump_reg16(REG_DPLL, "REG_DPLL"); + dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK"); + dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK"); + dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST"); + dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV"); +} + +void calypso_pll_set(uint16_t inp) +{ + uint8_t mult = inp >> 8; + uint8_t div = inp & 0xff; + uint16_t reg = readw(REG_DPLL); + + reg &= ~0x0fe0; + reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT; + reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT; + reg |= DPLL_PLL_ENABLE; + + writew(reg, REG_DPLL); +} + +void calypso_reset_set(enum calypso_rst calypso_rst, int active) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (active) + reg |= calypso_rst; + else + reg &= ~calypso_rst; + + writeb(reg, CLKM_REG(CNTL_RST)); +} + +int calypso_reset_get(enum calypso_rst calypso_rst) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (reg & calypso_rst) + return 1; + else + return 0; +} + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div) +{ + uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK)); + uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK)); + + /* First set the vtcxo_div2 */ + cntl_clock &= ~CLK_VCLKOUT_DIV2; + if (vtcxo_div2) + cntl_clock |= CLK_VTCXO_DIV2; + else + cntl_clock &= ~CLK_VTCXO_DIV2; + writew(cntl_clock, CLKM_REG(CNTL_CLK)); + + /* Then configure the MCLK divider */ + cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0; + if (mclk_div & 0x80) { + mclk_div &= ~0x80; + cntl_arm_clk |= ARM_CLK_MCLK_DIV5; + } else + cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5; + cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT); + cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT); + writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK)); + + /* Then finally set the PLL */ + calypso_pll_set(inp); +} + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we) +{ + writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7), + BASE_ADDR_MEMIF + bank); +} + +void calypso_bootrom(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + conf &= ~(3 << 8); + + // XXX: this can't be correct + if (enable) + conf |= (1 << 8); + else + conf |= (1 << 8); + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +void calypso_debugunit(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + if (enable) + conf &= ~(1 << 11); + else + conf |= (1 << 11); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +#define REG_RHEA_CNTL 0xfffff900 +#define REG_API_CNTL 0xfffff902 +#define REG_ARM_RHEA 0xfffff904 + +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1) +{ + writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL); + writew(ws_h | (ws_l << 5), REG_API_CNTL); + writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA); +} diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c new file mode 100644 index 000000000..35c5be826 --- /dev/null +++ b/src/target/firmware/calypso/dma.c @@ -0,0 +1,44 @@ +/* Driver for Calypso DMA controller */ + +/* (C) 2010 by Harald Welte + * + * 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 + +#define BASE_ADDR_DMA 0xfffffc00 + +enum dma_reg { + CONTROLLER_CONF = 0x00, + ALLOC_CONFIG = 0x02, +}; +#define DMA_REG(m) (BASE_ADDR_DMA + (m)) + +#define DMA_RAD(x) DMA_REG((x)*0x10 + 0x0) +#define DMA_RDPATH(x) DMA_REG((x)*0x10 + 0x2) +#define DMA_AAD(x) DMA_REG((x)*0x10 + 0x4) +#define DMA_ALGTH(x) DMA_REG((x)*0x10 + 0x6) +#define DMA_CTRL(x) DMA_REG((x)*0x10 + 0x8) +#define DMA_CUR_OFF_API(x) DMA_REG((x)*0x10 + 0xa) + +void dma_init(void) +{ + /* DMA 1 (RIF Tx), 2 (RIF Rx) allocated to DSP, all others to ARM */ + writew(0x000c, DMA_REG(ALLOC_CONFIG)); +} diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c new file mode 100644 index 000000000..59176567c --- /dev/null +++ b/src/target/firmware/calypso/dsp.c @@ -0,0 +1,391 @@ +#define DEBUG +/* Driver for the Calypso integrated DSP */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#define REG_API_CONTROL 0xfffe0000 +#define APIC_R_SMODE_HOM (1 << 1) /* API is configured in HOM mode */ +#define APIC_R_HINT (1 << 3) /* Host processor interrupt (DSP->MCU) */ +#define APIC_W_DSPINT (1 << 2) /* ARM issues interrupt to DSP */ + +#define REG_API_WS 0xfffff902 /* Number of wait states for ARM access to API memory */ +#define REG_ARM_RHEA_CTL 0xfffff904 /* Write buffer bypassing */ +#define REG_EXT_RHEA_CTL 0xfffff906 /* Some timeout */ + +#define API_SIZE 0x2000U /* in words */ + +#define BASE_API_RAM 0xffd00000 /* Base address of API RAM form ARM point of view */ + +#define DSP_BASE_API 0x0800 /* Base address of API RAM for DSP */ +#define DSP_BASE_API_MIRROR 0xe000 /* Base address of API RAM for DSP (API boot mirrot */ +#define DSP_START 0x7000 /* DSP Start address */ + +/* Boot loader */ +#define BL_CMD_STATUS (BASE_API_RAM + 0x0ffe) /* Status / Command var */ +#define BL_ADDR_LO (BASE_API_RAM + 0x0ffc) /* Address (16 lsbs) */ +#define BL_ADDR_HI (BASE_API_RAM + 0x0ff8) /* Address (ext page bits) */ +#define BL_SIZE (BASE_API_RAM + 0x0ffa) /* Size */ + +#define BL_MAX_BLOCK_SIZE 0x7F0 /* Maximum size of copied block */ + + /* Possible values for the download status */ +#define BL_STATUS_NA 0 +#define BL_STATUS_IDLE 1 +#define BL_CMD_COPY_BLOCK 2 +#define BL_CMD_COPY_MODE 4 + +#define BL_MODE_PROG_WRITE 0 +#define BL_MODE_DATA_WRITE 1 +#define BL_MODE_PROG_READ 2 +#define BL_MODE_DATA_READ 3 +#define BL_MODE_PROM_READ 4 +#define BL_MODE_DROM_READ 5 + + +struct dsp_section { + uint32_t addr; /* addr for DSP */ + uint32_t size; /* size in words */ + const uint16_t *data; +}; + +#include "dsp_params.c" +#include "dsp_bootcode.c" +#include "dsp_dumpcode.c" + +struct dsp_api dsp_api = { + .ndb = (T_NDB_MCU_DSP *) BASE_API_NDB, + .db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0, + .db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0, + .param = (T_PARAM_MCU_DSP *) BASE_API_PARAM, + .r_page = 0, + .w_page = 0, +}; + + +void dsp_dump_version(void) +{ + printf("DSP Download Status: 0x%04x\n", readw(BL_CMD_STATUS)); + printf("DSP API Version: 0x%04x 0x%04x\n", + dsp_api.ndb->d_version_number1, dsp_api.ndb->d_version_number2); +} + +static void dsp_bl_wait_ready(void) +{ + while (readw(BL_CMD_STATUS) != BL_STATUS_IDLE); +} + +static int dsp_upload_sections_api(const struct dsp_section *sec, uint16_t dsp_base_api) +{ + for (; sec->data; sec++) { + unsigned int i; + volatile uint16_t *dptr; + + if (sec->addr & ~((1<<16)-1)) /* 64k max addr */ + return -1; + if (sec->addr < dsp_base_api) + return -1; + if ((sec->addr + sec->size) > (dsp_base_api + API_SIZE)) + return -1; + + dptr = (volatile uint16_t *)(BASE_API_RAM + ((sec->addr - dsp_base_api) * sizeof(uint16_t))); + for (i=0; isize; i++) + *dptr++ = sec->data[i]; + } + + /* FIXME need eioio or wb ? */ + + return 0; +} + +static void dsp_pre_boot(const struct dsp_section *bootcode) +{ + dputs("Assert DSP into Reset\n"); + calypso_reset_set(RESET_DSP, 1); + + if (bootcode) { + dputs("Loading initial DSP bootcode (API boot mode)\n"); + dsp_upload_sections_api(dsp_bootcode, DSP_BASE_API_MIRROR); + + writew(BL_STATUS_NA, BL_CMD_STATUS); + } else + delay_ms(10); + + dputs("Releasing DSP from Reset\n"); + calypso_reset_set(RESET_DSP, 0); + + /* Wait 10 us */ + delay_ms(100); + + dsp_bl_wait_ready(); +} + +static void dsp_set_params(int16_t *param_tab, int param_size) +{ + int i; + int16_t *param_ptr = (int16_t *) BASE_API_PARAM; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* FIXME: Implement Patch download, if any */ + + dputs("Setting some dsp_api.ndb values\n"); + dsp_api.ndb->d_background_enable = 0; + dsp_api.ndb->d_background_abort = 0; + dsp_api.ndb->d_background_state = 0; + dsp_api.ndb->d_debug_ptr = 0x0074; + dsp_api.ndb->d_debug_bk = 0x0001; + dsp_api.ndb->d_pll_config = 0x154; //C_PLL_CONFIG; + dsp_api.ndb->p_debug_buffer = 0x17ff; //C_DEBUG_BUFFER_ADD; + dsp_api.ndb->d_debug_buffer_size = 7; //C_DEBUG_BUFFER_SIZE; + dsp_api.ndb->d_debug_trace_type = 0; //C_DEBUG_TRACE_TYPE; + dsp_api.ndb->d_dsp_state = 3; //C_DSP_IDLE3; + dsp_api.ndb->d_audio_gain_ul = 0; + dsp_api.ndb->d_audio_gain_dl = 0; + dsp_api.ndb->d_es_level_api = 0x5213; + dsp_api.ndb->d_mu_api = 0x5000; + + dputs("Setting API NDB parameters\n"); + for (i = 0; i < param_size; i ++) + *param_ptr++ = param_tab[i]; + + dsp_dump_version(); + + dputs("Finishing download phase\n"); + writew(0, BL_SIZE); + writew(DSP_START, BL_ADDR_LO); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_dump_version(); +} + +void dsp_api_memset(uint16_t *ptr, int octets) +{ + uint16_t i; + for (i = 0; i < octets / sizeof(uint16_t); i++) + *ptr++ = 0; +} + +static void dsp_ndb_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + + ndb->d_fb_mode = 1; /* mode 1 FCCH burst detection */ + ndb->d_fb_det = 0; /* we have not yet detected a FB */ + ndb->a_cd[0] = (1<a_dd_0[0] = 0; + ndb->a_dd_0[2] = 0xffff; + ndb->a_dd_1[0] = 0; + ndb->a_dd_1[2] = 0xffff; + ndb->a_du_0[0] = 0; + ndb->a_du_0[2] = 0xffff; + ndb->a_du_1[0] = 0; + ndb->a_du_1[2] = 0xffff; + ndb->a_fd[0] = (1<a_fd[2] = 0xffff; + ndb->d_a5mode = 0; + ndb->d_tch_mode = 0x0800; + /* FIXME: set guard bits */ + ndb->a_sch26[0] = (1<d_spcx_rif = 0x179; +} + +static void dsp_db_init(void) +{ + dsp_api_memset((void *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((void *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((void *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU)); + dsp_api_memset((void *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU)); +} + +void dsp_power_on(void) +{ + dsp_set_params((int16_t *)&dsp_params, sizeof(dsp_params)/2); + dsp_ndb_init(); + dsp_db_init(); + dsp_api.frame_ctr = 0; + dsp_api.r_page = dsp_api.w_page = dsp_api.r_page_used = 0; +} + +/* test for frequency burst detection */ +#define REG_INT_STAT 0xffff1004 +static void wait_for_frame_irq(void) +{ + //puts("Waiting for Frame Interrupt"); + //while (readb(REG_INT_STAT) & 1) + while (readb((void *)0xffff1000) & (1<<4)) + ;// putchar('.'); + //puts("Done!\n"); +} + +void dsp_end_scenario(void) +{ + /* FIXME: we don't yet deal with the MISC_TASK */ + + /* End the DSP Scenario */ + dsp_api.ndb->d_dsp_page = B_GSM_TASK | dsp_api.w_page; + dsp_api.w_page ^= 1; + + /* Tell TPU to generate a FRAME interrupt to the DSP */ + tpu_dsp_frameirq_enable(); + tpu_frame_irq_en(1, 1); +} + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_d = task; + dsp_api.db_w->d_burst_d = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_u = task; + dsp_api.db_w->d_burst_u = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +#define SC_CHKSUM_VER (BASE_API_W_PAGE_0 + (2 * (0x08DB - 0x800))) +static void dsp_dump_csum(void) +{ + printf("dsp page : %u\n", dsp_api.ndb->d_dsp_page); + printf("dsp code version : 0x%04x\n", dsp_api.db_r->a_pm[0]); + printf("dsp checksum : 0x%04x\n", dsp_api.db_r->a_pm[1]); + printf("dsp patch version : 0x%04x\n", readw(SC_CHKSUM_VER)); +} + +void dsp_checksum_task(void) +{ + dsp_dump_csum(); + dsp_api.db_w->d_task_md = CHECKSUM_DSP_TASK; + dsp_api.ndb->d_fb_mode = 1; + + dsp_end_scenario(); + + wait_for_frame_irq(); + + dsp_dump_csum(); +} + +#define L1D_AUXAPC 0x0012 +#define L1D_APCRAM 0x0014 + +void dsp_load_apc_dac(uint16_t apc) +{ + dsp_api.db_w->d_power_ctl = (apc << 6) | L1D_AUXAPC; +} + + +static void _dsp_dump_range(uint32_t addr, uint32_t size, int mode) +{ + uint32_t bs; + + /* Mode selection */ + writew(mode, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Block by block dump */ + while (size) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + + bs = (size > BL_MAX_BLOCK_SIZE) ? BL_MAX_BLOCK_SIZE : size; + size -= bs; + + writew(addr >> 16, BL_ADDR_HI); + writew(addr & 0xffff, BL_ADDR_LO); + writew(bs, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_bl_wait_ready(); + + while (bs--) { + if ((addr&15)==0) + printf("%05ux : ", addr); + printf("%04hx%c", *api++, ((addr&15)==15)?'\n':' '); + addr++; + } + }; + puts("\n"); +} + +void dsp_dump(void) +{ + static const struct { + const char *name; + uint32_t addr; + uint32_t size; + int mode; + } dr[] = { + { "Registers", 0x00000, 0x0060, BL_MODE_DATA_READ }, + { "DROM", 0x09000, 0x5000, BL_MODE_DROM_READ }, + { "PDROM", 0x0e000, 0x2000, BL_MODE_DROM_READ }, + { "PROM0", 0x07000, 0x7000, BL_MODE_PROM_READ }, + { "PROM1", 0x18000, 0x8000, BL_MODE_PROM_READ }, + { "PROM2", 0x28000, 0x8000, BL_MODE_PROM_READ }, + { "PROM3", 0x38000, 0x2000, BL_MODE_PROM_READ }, + { NULL, 0, 0, -1 } + }; + + int i; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* Load and execute our dump code in the DSP */ + dsp_upload_sections_api(dsp_dumpcode, DSP_BASE_API); + + writew(0, BL_ADDR_HI); + writew(DSP_DUMPCODE_START, BL_ADDR_LO); + writew(0, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + /* our dump code actually simulates the boot loaded + * but with added read commands */ + dsp_bl_wait_ready(); + + /* Test the 'version' command */ + writew(0xffff, BL_CMD_STATUS); + dsp_bl_wait_ready(); + printf("DSP bootloader version 0x%04x\n", readw(BASE_API_RAM)); + + /* Dump each range */ + for (i=0; dr[i].name; i++) { + printf("DSP dump: %s [%05ux-%05ux]\n", dr[i].name, + dr[i].addr, dr[i].addr+dr[i].size-1); + _dsp_dump_range(dr[i].addr, dr[i].size, dr[i].mode); + } +} + diff --git a/src/target/firmware/calypso/dsp_bootcode.c b/src/target/firmware/calypso/dsp_bootcode.c new file mode 100644 index 000000000..2db46568f --- /dev/null +++ b/src/target/firmware/calypso/dsp_bootcode.c @@ -0,0 +1,9 @@ +/* Calypso integrated DSP boot code */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +/* We don't really need any DSP boot code, it happily works with its own ROM */ +static const struct dsp_section *dsp_bootcode = NULL; + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_dumpcode.c b/src/target/firmware/calypso/dsp_dumpcode.c new file mode 100644 index 000000000..265a1c127 --- /dev/null +++ b/src/target/firmware/calypso/dsp_dumpcode.c @@ -0,0 +1,45 @@ +/* Generated from src/target_dsp/calypso/dsp_dump.bin */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_dumpcode[] = { + { + .addr = 0x1000, + .size = 0x005b, + .data = _SA_DECL { + 0x69f8, 0x0029, 0x0002, 0xea1f, + 0x7718, 0x1100, 0x7714, 0x0000, + 0x7712, 0x0800, 0x767f, 0x0001, + 0x607f, 0xffff, 0xf820, 0x1014, + 0xf273, 0x1008, 0x7682, 0x0100, + 0x607f, 0x0004, 0xf820, 0x101c, + 0xf273, 0x1008, 0x7214, 0x0800, + 0x607f, 0x0002, 0xf820, 0x100c, + 0x127e, 0x8813, 0x3c7c, 0x137d, + 0x8911, 0xf84c, 0x1028, 0xf4e2, + 0x7715, 0x0014, 0x963d, 0xfa30, + 0x104b, 0x6d89, 0x963f, 0xfa30, + 0x103f, 0x963e, 0xf495, 0xf830, + 0x103a, 0x47f8, 0x0011, 0x7f92, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0x7e92, 0xf073, 0x1008, 0xf830, + 0x1046, 0x47f8, 0x0011, 0xe589, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0xe598, 0xf073, 0x1008, 0x4911, + 0x891a, 0xf830, 0x1055, 0xf072, + 0x1052, 0xf074, 0x7213, 0xf073, + 0x1008, 0xf072, 0x1058, 0xf074, + 0xe4b8, 0xf073, 0x1008, + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#define DSP_DUMPCODE_START 0x1000 + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_params.c b/src/target/firmware/calypso/dsp_params.c new file mode 100644 index 000000000..ec44a0ed0 --- /dev/null +++ b/src/target/firmware/calypso/dsp_params.c @@ -0,0 +1,94 @@ +/* Values from an actual phone firmware that uses the 3306 DSP ROM code version */ +static T_PARAM_MCU_DSP dsp_params = { + .d_transfer_rate = 0x6666, + /* Latencies */ + .d_lat_mcu_bridge = 15, + .d_lat_mcu_hom2sam = 12, + .d_lat_mcu_bef_fast_access = 5, + .d_lat_dsp_after_sam = 4, + /* DSP Start Address */ + .d_gprs_install_address = 0x7002, /* needs to be set by patch or manually */ + .d_misc_config = 1, + .d_cn_sw_workaround = 0xE, + .d_hole2_param = { 0, 0, 0, 0 }, + /* Frequency Burst */ + .d_fb_margin_beg = 24, + .d_fb_margin_end = 22, + .d_nsubb_idle = 296, + .d_nsubb_dedic = 30, + .d_fb_thr_det_iacq = 0x3333, + .d_fb_thr_det_track = 0x28f6, + /* Demodulation */ + .d_dc_off_thres = 0x7fff, + .d_dummy_thres = 17408, + .d_dem_pond_gewl = 26624, + .d_dem_pond_red = 20152, + /* TCH Full Speech */ + .d_maccthresh1 = 7872, + .d_mldt = -4, + .d_maccthresh = 7172, + .d_gu = 5772, + .d_go = 7872, + .d_attmax = 53, + .d_sm = -892, + .d_b = 208, + /* V.42 bis */ + .d_v42b_switch_hyst = 16, + .d_v42b_switch_min = 64, + .d_v42b_switch_max = 250, + .d_v42b_reset_delay = 10, + /* TCH Half Speech */ + .d_ldT_hr = -5, + .d_maccthresh_hr = 6500, + .d_maccthresh1_hr = 6500, + .d_gu_hr = 2620, + .d_go_hr = 3700, + .d_b_hr = 182, + .d_sm_hr = -1608, + .d_attmax_hr = 53, + /* TCH Enhanced FR Speech */ + .c_mldt_efr = -4, + .c_maccthresh_efr = 8000, + .c_maccthresh1_efr = 8000, + .c_gu_efr = 4522, + .c_go_efr = 6500, + .c_b_efr = 174, + .c_sm_efr = -878, + .c_attmax_efr = 53, + /* CHED TCH Full Speech */ + .d_sd_min_thr_tchfs = 15, + .d_ma_min_thr_tchfs = 738, + .d_md_max_thr_tchfs = 1700, + .d_md1_max_thr_tchfs = 99, + /* CHED TCH Half Speech */ + .d_sd_min_thr_tchhs = 37, + .d_ma_min_thr_tchhs = 344, + .d_sd_av_thr_tchhs = 1845, + .d_md_max_thr_tchhs = 2175, + .d_md1_max_thr_tchhs = 138, + /* CHED TCH/F EFR Speech */ + .d_sd_min_thr_tchefs = 15, + .d_ma_min_thr_tchefs = 738, + .d_md_max_thr_tchefs = 0x4ce, + .d_md1_max_thr_tchefs = 0x63, + /* */ + .d_wed_fil_ini = 0x122a, + .d_wed_fil_tc = 0x7c00, + .d_x_min = 0xf, + .d_x_max = 0x17, + .d_slope = 0x87, + .d_y_min = 0x2bf, + .d_y_max = 0x99c, + .d_wed_diff_threshold = 0x196, + .d_mabfi_min_thr_tchhs = 0x14c8, + /* FACCH module */ + .d_facch_thr = 0, + /* IDS module */ + .d_max_ovsp_ul = 8, + .d_sync_thres = 0x3f50, + .d_idle_thres = 0x4000, + .d_m1_thres = 5, + .d_max_ovsp_dl = 8, + .d_gsm_bgd_mgt = 0, + /* we don't set the FIR coefficients !?! */ +}; diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c new file mode 100644 index 000000000..58783b064 --- /dev/null +++ b/src/target/firmware/calypso/du.c @@ -0,0 +1,51 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht + * + * 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 +#include +#include + +#include + +#define BASE_ADDR_DU 0x03c00000 +#define DU_REG(m) (BASE_ADDR_DU+(m)) + +void calypso_du_init() { + unsigned char c; + calypso_debugunit(1); + for(c = 0; c < 64; c++) { + writew(DU_REG(c), 0x00000000); + } +} + +void calypso_du_stop() { + calypso_debugunit(0); +} + +void calypso_du_dump() { + unsigned char c; + puts("Debug unit traceback:\n"); + for(c = 0; c < 64; c++) { + uint32_t w = readw(DU_REG(c)); + printf("t-%2x: 0x%8x\n", c, (unsigned int)w); + } +} diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c new file mode 100644 index 000000000..a46fd72a8 --- /dev/null +++ b/src/target/firmware/calypso/i2c.c @@ -0,0 +1,123 @@ +/* Driver for I2C Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include + +#define BASE_ADDR_I2C 0xfffe2800 +#define I2C_REG(x) (BASE_ADDR_I2C+(x)) + +enum i2c_reg { + DEVICE_REG = 0, + ADDRESS_REG, + DATA_WR_REG, + DATA_RD_REG, + CMD_REG, + CONF_FIFO_REG, + CONF_CLK_REG, + CONF_CLK_FUNC_REF, + STATUS_FIFO_REG, + STATUS_ACTIVITY_REG, +}; + +#define I2C_CMD_SOFT_RESET (1 << 0) +#define I2C_CMD_EN_CLK (1 << 1) +#define I2C_CMD_START (1 << 2) +#define I2C_CMD_RW_READ (1 << 3) +#define I2C_CMD_COMP_READ (1 << 4) +#define I2C_CMD_IRQ_ENABLE (1 << 5) + +#define I2C_STATUS_ERROR_DATA (1 << 0) +#define I2C_STATUS_ERROR_DEV (1 << 1) +#define I2C_STATUS_IDLE (1 << 2) +#define I2C_STATUS_INTERRUPT (1 << 3) + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len) +{ + uint8_t cmd; + + /* Calypso I2C controller doesn't support fancy addressing */ + if (alen > 1) + return -1; + + /* FIXME: implement writes longer than fifo size */ + if (len > 16) + return -1; + + printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr) + + writeb(chip & 0x3f, I2C_REG(DEVICE_REG)); + writeb(addr & 0xff, I2C_REG(ADDRESS_REG)); + + /* we have to tell the controler how many bits we'll put into the fifo ?!? */ + writeb(len-1, I2C_REG(CONF_FIFO_REG)); + + /* fill the FIFO */ + while (len--) { + uint8_t byte = *buffer++; + writeb(byte, I2C_REG(DATA_WR_REG)); + printd("%02X ", byte); + } + dputchar('\n'); + + /* start the transfer */ + cmd = readb(I2C_REG(CMD_REG)); + cmd |= I2C_CMD_START; + writeb(cmd, I2C_REG(CMD_REG)); + + /* wait until transfer completes */ + while (1) { + uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG)); + printd("I2C Status: 0x%02x\n", rerg & 0xf); + if (reg & I2C_STATUS_IDLE) + break; + } + dputs("I2C transfer completed\n"); + + return 0; +} + +void i2c_init(int speed, int slaveadd) +{ + /* scl_out = clk_func_ref / 3, + clk_func_ref = master_clock_freq / (divisor_2 + 1) + master_clock_freq = ext_clock_freq / divisor_1 */ + /* clk_func_ref = scl_out * 3, + divisor_2 = (master_clock_freq / clk_func_ref) - 1 + divisor_1 = ext_clock_freq / master_clock_freq */ + /* for a target freq of 200kHz: + ext_clock_freq = 13MHz + clk_func_ref = 3 * 300kHZ = 600kHz + divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz + divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz + scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */ + writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG)); + + writeb(0x00, I2C_REG(CONF_CLK_REG)); + writeb(21, I2C_REG(CONF_CLK_FUNC_REF)); + + writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG)); +} diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c new file mode 100644 index 000000000..d019918df --- /dev/null +++ b/src/target/firmware/calypso/irq.c @@ -0,0 +1,266 @@ +/* Driver for Calypso IRQ controller */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#define BASE_ADDR_IRQ 0xfffffa00 + +enum irq_reg { + IT_REG1 = 0x00, + IT_REG2 = 0x02, + MASK_IT_REG1 = 0x08, + MASK_IT_REG2 = 0x0a, + IRQ_NUM = 0x10, + FIQ_NUM = 0x12, + IRQ_CTRL = 0x14, +}; + +#define ILR_IRQ(x) (0x20 + (x*2)) +#define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x)) + +#define NR_IRQS 32 + +static uint8_t default_irq_prio[] = { + [IRQ_WATCHDOG] = 0xff, + [IRQ_TIMER1] = 0xff, + [IRQ_TIMER2] = 0xff, + [IRQ_TSP_RX] = 0, + [IRQ_TPU_FRAME] = 3, + [IRQ_TPU_PAGE] = 0xff, + [IRQ_SIMCARD] = 0xff, + [IRQ_UART_MODEM] = 8, + [IRQ_KEYPAD_GPIO] = 4, + [IRQ_RTC_TIMER] = 9, + [IRQ_RTC_ALARM_I2C] = 10, + [IRQ_ULPD_GAUGING] = 2, + [IRQ_EXTERNAL] = 12, + [IRQ_SPI] = 0xff, + [IRQ_DMA] = 0xff, + [IRQ_API] = 0xff, + [IRQ_SIM_DETECT] = 0, + [IRQ_EXTERNAL_FIQ] = 7, + [IRQ_UART_IRDA] = 2, + [IRQ_ULPD_GSM_TIMER] = 1, + [IRQ_GEA] = 0xff, +}; + +static irq_handler *irq_handlers[NR_IRQS]; + +static void _irq_enable(enum irq_nr nr, int enable) +{ + uint16_t *reg = IRQ_REG(MASK_IT_REG1); + uint16_t val; + + if (nr > 15) { + reg = IRQ_REG(MASK_IT_REG2); + nr -= 16; + } + + val = readw(reg); + if (enable) + val &= ~(1 << nr); + else + val |= (1 << nr); + writew(val, reg); +} + +void irq_enable(enum irq_nr nr) +{ + _irq_enable(nr, 1); +} + +void irq_disable(enum irq_nr nr) +{ + _irq_enable(nr, 0); +} + +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio) +{ + uint16_t val; + + if (prio == -1) + prio = default_irq_prio[nr]; + + if (prio > 31) + prio = 31; + + val = prio << 2; + if (edge) + val |= 0x02; + if (fiq) + val |= 0x01; + + writew(val, IRQ_REG(ILR_IRQ(nr))); +} + +/* Entry point for interrupts */ +void irq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + +#if 1 + /* Hardware interrupt detection mode */ + num = readb(IRQ_REG(IRQ_NUM)) & 0x1f; + + printd("i%02x\n", num); + + handler = irq_handlers[num]; + + if (handler) + handler(num); +#else + /* Software interrupt detection mode */ + { + uint16_t it_reg, mask_reg; + uint32_t irqs; + + it_reg = readw(IRQ_REG(IT_REG1)); + mask_reg = readw(IRQ_REG(MASK_IT_REG1)); + irqs = it_reg & ~mask_reg; + + it_reg = readw(IRQ_REG(IT_REG2)); + mask_reg = readw(IRQ_REG(MASK_IT_REG2)); + irqs |= (it_reg & ~mask_reg) << 16; + + for (num = 0; num < 32; num++) { + if (irqs & (1 << num)) { + printd("i%d\n", num); + handler = irq_handlers[num]; + if (handler) + handler(num); + /* clear this interrupt */ + if (num < 16) + writew(~(1 << num), IRQ_REG(IT_REG1)); + else + writew(~(1 << (num-16)), IRQ_REG(IT_REG2)); + } + } + dputchar('\n'); + } +#endif + /* Start new IRQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x01; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +/* Entry point for FIQs */ +void fiq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + + num = readb(IRQ_REG(FIQ_NUM)) & 0x1f; + if (num) { + printd("f%02x\n", num); + } + + handler = irq_handlers[num]; + + if (handler) + handler(num); + + /* Start new FIQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x02; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +void irq_register_handler(enum irq_nr nr, irq_handler *handler) +{ + if (nr > NR_IRQS) + return; + + irq_handlers[nr] = handler; +} + +#define BASE_ADDR_IBOOT_EXC 0x0080001C +extern uint32_t _exceptions; + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void) +{ + uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC; + uint32_t *exceptions_src = &_exceptions; + int i; + + for (i = 0; i < 7; i++) + *exceptions_dst++ = *exceptions_src++; + +} + +static void set_default_priorities(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) { + uint16_t val; + uint8_t prio = default_irq_prio[i]; + if (prio > 31) + prio = 31; + + val = readw(IRQ_REG(ILR_IRQ(i))); + val &= ~(0x1f << 2); + val |= prio << 2; + writew(val, IRQ_REG(ILR_IRQ(i))); + } +} + +static uint32_t irq_nest_mask; +/* mask off all interrupts that have a lower priority than irq_nr */ +static void mask_all_lower_prio_irqs(enum irq_nr irq) +{ + uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irq))) >> 2; + int i; + + for (i = 0; i < _NR_IRQ; i++) { + uint8_t prio; + + if (i == irq) + continue; + + prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2; + if (prio >= our_prio) + irq_nest_mask |= (1 << i); + } +} + +void irq_init(void) +{ + /* set default priorities */ + set_default_priorities(); + /* mask all interrupts off */ + writew(0xffff, IRQ_REG(MASK_IT_REG1)); + writew(0xffff, IRQ_REG(MASK_IT_REG2)); + /* clear all pending interrupts */ + writew(0, IRQ_REG(IT_REG1)); + writew(0, IRQ_REG(IT_REG2)); + /* enable interrupts globally to the ARM core */ + arm_enable_interrupts(); +} diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c new file mode 100644 index 000000000..27f48c0bc --- /dev/null +++ b/src/target/firmware/calypso/keypad.c @@ -0,0 +1,165 @@ +/* Driver for the keypad attached to the TI Calypso */ + +/* (C) 2010 by roh + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + + +#define KBR_LATCH_REG 0xfffe480a +#define KBC_REG 0xfffe480c +#define KBD_GPIO_INT 0xfffe4816 +#define KBD_GPIO_MASKIT 0xfffe4818 + +static key_handler_t key_handler = NULL; + +void emit_key(uint8_t key, uint8_t state) +{ + printf("key=%u %s\n", key, state == PRESSED ? "pressed" : "released"); + + if (state == RELEASED) + if (key == KEY_POWER) + twl3025_power_off(); + + if(key_handler) { + key_handler(key, state); + } +} + +volatile uint32_t lastbuttons; + +#define BTN_TO_KEY(name) \ + ((diff & BTN_##name) == BTN_##name) \ + { \ + key = KEY_##name; \ + diff = diff & ~BTN_##name; \ + } + +void dispatch_buttons(uint32_t buttons) +{ + uint8_t state; + + if (buttons == lastbuttons) + return; + + if (buttons > lastbuttons) + state = PRESSED; + else + state = RELEASED; + + uint32_t diff = buttons ^ lastbuttons; + uint8_t key=KEY_INV; + + while (diff != 0) + { + if BTN_TO_KEY(POWER) + else if BTN_TO_KEY(0) + else if BTN_TO_KEY(1) + else if BTN_TO_KEY(2) + else if BTN_TO_KEY(3) + else if BTN_TO_KEY(4) + else if BTN_TO_KEY(5) + else if BTN_TO_KEY(6) + else if BTN_TO_KEY(7) + else if BTN_TO_KEY(8) + else if BTN_TO_KEY(9) + else if BTN_TO_KEY(STAR) + else if BTN_TO_KEY(HASH) + else if BTN_TO_KEY(MENU) + else if BTN_TO_KEY(LEFT_SB) + else if BTN_TO_KEY(RIGHT_SB) + else if BTN_TO_KEY(UP) + else if BTN_TO_KEY(DOWN) + else if BTN_TO_KEY(LEFT) + else if BTN_TO_KEY(RIGHT) + else if BTN_TO_KEY(OK) + else + { + printf("\nunknown keycode: 0x%08x\n", diff); + break; + } + if ( key == KEY_POWER ) + diff = 0; + emit_key(key, state); + } + lastbuttons = buttons; +} + +static void keypad_irq(enum irq_nr nr) +{ + keypad_scan(); +} + +void keypad_init() +{ + lastbuttons = 0; + writew(0, KBD_GPIO_MASKIT); + writew(0, KBC_REG); + + irq_register_handler(IRQ_KEYPAD_GPIO, &keypad_irq); + irq_config(IRQ_KEYPAD_GPIO, 0, 0, 0); + irq_enable(IRQ_KEYPAD_GPIO); +} + +void keypad_set_handler(key_handler_t handler) +{ + key_handler = handler; +} + +void keypad_scan() +{ + uint16_t reg; + uint16_t col; + uint32_t buttons; + +// putchar('\n'); + buttons = 0x0; + //scan for BTN_POWER + writew(0xff, KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); +// printd("%02x ", (~reg & 0x1f)); + buttons = buttons | ((~reg & 0x1f) << 20 ); + + //scan for muxed keys if not powerbtn + if ((~reg & 0x1f) != 0x10) + for (col=0;col<4;col++) + { + writew(0x1f & ~(0x1 << col ), KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); + buttons = buttons | ((~reg & 0x1f) << (col * 5 )); +// printd("%02x ", (~reg & 0x1f)); + } + //enable keypad irq via master 'or' gate (needs col lines low in idle to work) + writew(0, KBC_REG); + dispatch_buttons(buttons); + +} + diff --git a/src/target/firmware/calypso/misc.c b/src/target/firmware/calypso/misc.c new file mode 100644 index 000000000..460cc5d51 --- /dev/null +++ b/src/target/firmware/calypso/misc.c @@ -0,0 +1,60 @@ + +#include +#include +#include + +/* dump a memory range */ +void memdump_range(unsigned int *ptr, unsigned int len) +{ + unsigned int *end = ptr + (len/4); + unsigned int *tmp; + + for (tmp = ptr; tmp < end; tmp += 8) { + int i; + printf("%08X: ", (unsigned int) tmp); + + for (i = 0; i < 8; i++) + printf("%08X %s", *(tmp+i), i == 3 ? " " : ""); + + putchar('\n'); + } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) +void dump_mem(void) +{ + puts("Dump 64kBits of internal ROM\n"); + memdump_range((void *)0x03800000, 64*KBIT/8); + + puts("Dump 8Mbits of external flash\n"); + memdump_range((void *)0x00000000, 8*MBIT/8); + + puts("Dump 2Mbits of internal RAM\n"); + memdump_range((void *)0x00800000, 2*MBIT/8); + + puts("Dump 2Mbits of external RAM\n"); + memdump_range((void *)0x01000000, 2*MBIT/8); +} + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +void dump_dev_id(void) +{ + int i; + + printf("Device ID code: 0x%04x\n", readw(REG_DEV_ID_CODE)); + printf("Device Version code: 0x%04x\n", readw(REG_DEV_VER_CODE)); + printf("ARM ID code: 0x%04x\n", readw(REG_DEV_ARMVER_CODE)); + printf("cDSP ID code: 0x%04x\n", readw(REG_cDSP_ID_CODE)); + puts("Die ID code: "); + for (i = 0; i < 64/8; i += 4) + printf("%08x", readl(REG_DIE_ID_CODE+i)); + putchar('\n'); +} + + diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c new file mode 100644 index 000000000..afa3824be --- /dev/null +++ b/src/target/firmware/calypso/rtc.c @@ -0,0 +1,82 @@ +/* Driver for Calypso RTC controller */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#define BASE_ADDR_RTC 0xfffe1800 +#define RTC_REG(x) ((void *)BASE_ADDR_RTC + (x)) + +enum rtc_reg { + SECOND_REG = 0x00, + MINUTES_REG = 0x01, + HOURS_REG = 0x02, + DAYS_REG = 0x03, + MONTHS_REG = 0x04, + YEARS_REG = 0x05, + WEEK_REG = 0x06, + /* reserved */ + ALARM_SECOND_REG = 0x08, + ALARM_MINUTES_REG = 0x09, + ALARM_HOURS_REG = 0x0a, + ALARM_DAYS_REG = 0x0b, + ALARM_MONTHS_REG = 0x0c, + ALARM_YEARS_REG = 0x0d, + /* reserved */ + /* reserved */ + CTRL_REG = 0x10, + STATUS_REG = 0x11, + INT_REG = 0x12, + COMP_LSB_REG = 0x13, + COMP_MSB_REG = 0x14, + RES_PROG_REG = 0x15, +}; + +static int tick_ctr; + +static void rtc_irq_tick(enum irq_nr nr) +{ + if (tick_ctr & 1) + st7558_set_attr(DISP_ATTR_INVERT); + else + st7558_unset_attr(DISP_ATTR_INVERT); + tick_ctr++; +} + +void rtc_init(void) +{ + irq_register_handler(IRQ_RTC_TIMER, &rtc_irq_tick); + irq_config(IRQ_RTC_TIMER, 0, 1, 0); + irq_enable(IRQ_RTC_TIMER); + + /* clear power-up reset */ + writeb(0x80, RTC_REG(STATUS_REG)); + /* enable RTC running */ + writeb(0x01, RTC_REG(CTRL_REG)); + /* enable periodic interrupts every second */ + writeb(0x04, RTC_REG(INT_REG)); +} diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c new file mode 100644 index 000000000..049ac0806 --- /dev/null +++ b/src/target/firmware/calypso/spi.c @@ -0,0 +1,141 @@ +/* Driver for SPI Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +//#define DEBUG +#include + +#include +#include +#include + +#define BASE_ADDR_SPI 0xfffe3000 +#define SPI_REG(n) (BASE_ADDR_SPI+(n)) + +enum spi_regs { + REG_SET1 = 0x00, + REG_SET2 = 0x02, + REG_CTRL = 0x04, + REG_STATUS = 0x06, + REG_TX_LSB = 0x08, + REG_TX_MSB = 0x0a, + REG_RX_LSB = 0x0c, + REG_RX_MSB = 0x0e, +}; + +#define SPI_SET1_EN_CLK (1 << 0) +#define SPI_SET1_WR_IRQ_DIS (1 << 4) +#define SPI_SET1_RDWR_IRQ_DIS (1 << 5) + +#define SPI_CTRL_RDWR (1 << 0) +#define SPI_CTRL_WR (1 << 1) +#define SPI_CTRL_NB_SHIFT 2 +#define SPI_CTRL_AD_SHIFT 7 + +#define SPI_STATUS_RE (1 << 0) /* Read End */ +#define SPI_STATUS_WE (1 << 1) /* Write End */ + +void spi_init(void) +{ + writew(SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS | SPI_SET1_RDWR_IRQ_DIS, + SPI_REG(REG_SET1)); + + writew(0x0001, SPI_REG(REG_SET2)); +} + +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din) +{ + uint8_t bytes_per_xfer; + uint8_t reg_status, reg_ctrl = 0; + uint32_t tmp; + + if (bitlen == 0) + return 0; + + if (bitlen > 32) + return -1; + + if (dev_idx > 4) + return -1; + + bytes_per_xfer = bitlen / 8; + if (bitlen % 8) + bytes_per_xfer ++; + + reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT; + reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT; + + if (bitlen <= 8) { + tmp = *(uint8_t *)dout; + tmp <<= 24 + (8-bitlen); /* align to MSB */ + } else if (bitlen <= 16) { + tmp = *(uint16_t *)dout; + tmp <<= 16 + (16-bitlen); /* align to MSB */ + } else { + tmp = *(uint32_t *)dout; + tmp <<= (32-bitlen); /* align to MSB */ + } + printd("spi_xfer(dev_idx=%u, bitlen=%u, data_out=0x%08x): ", + dev_idx, bitlen, tmp); + + /* fill transmit registers */ + writew(tmp >> 16, SPI_REG(REG_TX_MSB)); + writew(tmp & 0xffff, SPI_REG(REG_TX_LSB)); + + /* initiate transfer */ + if (din) + reg_ctrl |= SPI_CTRL_RDWR; + else + reg_ctrl |= SPI_CTRL_WR; + writew(reg_ctrl, SPI_REG(REG_CTRL)); + printd("reg_ctrl=0x%04x ", reg_ctrl); + + /* wait until the transfer is complete */ + while (1) { + reg_status = readw(SPI_REG(REG_STATUS)); + printd("status=0x%04x ", reg_status); + if (din && (reg_status & SPI_STATUS_RE)) + break; + else if (reg_status & SPI_STATUS_WE) + break; + } + /* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */ + delay_ms(1); + + if (din) { + tmp = readw(SPI_REG(REG_RX_MSB)) << 16; + tmp |= readw(SPI_REG(REG_RX_LSB)); + printd("data_in=0x%08x ", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + else + *(uint32_t *)din = tmp; + } + dputchar('\n'); + + return 0; +} diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c new file mode 100644 index 000000000..050c7c939 --- /dev/null +++ b/src/target/firmware/calypso/timer.c @@ -0,0 +1,127 @@ +/* Calypso DBB internal Timer Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include + +#define BASE_ADDR_TIMER 0xfffe3800 +#define TIMER2_OFFSET 0x3000 + +#define TIMER_REG(n, m) (((n)-1) ? (BASE_ADDR_TIMER + TIMER2_OFFSET + (m)) : (BASE_ADDR_TIMER + (m))) + +enum timer_reg { + CNTL_TIMER = 0x00, + LOAD_TIMER = 0x02, + READ_TIMER = 0x04, +}; + +enum timer_ctl { + CNTL_START = (1 << 0), + CNTL_AUTO_RELOAD = (1 << 1), + CNTL_CLOCK_ENABLE = (1 << 5), +}; + +/* Regular Timers (1 and 2) */ + +void hwtimer_enable(int num, int on) +{ + uint8_t ctl; + + if (num < 1 || num > 2) { + printf("Unknown timer %u\n", num); + return; + } + + ctl = readb(TIMER_REG(num, CNTL_TIMER)); + if (on) + ctl |= CNTL_START|CNTL_CLOCK_ENABLE; + else + ctl &= ~CNTL_START; + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload) +{ + uint8_t ctl; + + ctl = (pre_scale & 0x7) << 2; + if (auto_reload) + ctl |= CNTL_AUTO_RELOAD; + + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_load(int num, uint16_t val) +{ + writew(val, TIMER_REG(num, LOAD_TIMER)); +} + +uint16_t hwtimer_read(int num) +{ + uint8_t ctl = readb(TIMER_REG(num, CNTL_TIMER)); + + /* somehow a read results in an abort */ + if ((ctl & (CNTL_START|CNTL_CLOCK_ENABLE)) != (CNTL_START|CNTL_CLOCK_ENABLE)) + return 0xFFFF; + return readw(TIMER_REG(num, READ_TIMER)); +} + +void hwtimer_init(void) +{ + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(1, CNTL_TIMER)); + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(2, CNTL_TIMER)); +} + +/* Watchdog Timer */ + +#define BASE_ADDR_WDOG 0xfffff800 +#define WDOG_REG(m) (BASE_ADDR_WDOG + m) + +enum wdog_reg { + WD_CNTL_TIMER = CNTL_TIMER, + WD_LOAD_TIMER = LOAD_TIMER, + WD_READ_TIMER = 0x02, + WD_MODE = 0x04, +}; + +static void wdog_irq(enum irq_nr nr) +{ + puts("=> WATCHDOG\n"); +} + +void wdog_enable(int on) +{ + if (on) { + irq_config(IRQ_WATCHDOG, 0, 0, 0); + irq_register_handler(IRQ_WATCHDOG, &wdog_irq); + irq_enable(IRQ_WATCHDOG); + writew(0x8000, WDOG_REG(WD_MODE)); + } else { + writew(0xF5, WDOG_REG(WD_MODE)); + writew(0xA0, WDOG_REG(WD_MODE)); + } +} + diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c new file mode 100644 index 000000000..f0172e2ea --- /dev/null +++ b/src/target/firmware/calypso/tpu.c @@ -0,0 +1,288 @@ +/* Calypso DBB internal TPU (Time Processing Unit) Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include + +#define BASE_ADDR_TPU 0xffff1000 +#define TPU_REG(x) (BASE_ADDR_TPU+(x)) + +#define BASE_ADDR_TPU_RAM 0xffff9000 +#define TPU_RAM_END 0xffff97ff + +enum tpu_reg_arm { + TPU_CTRL = 0x0, /* Control & Status Register */ + INT_CTRL = 0x2, /* Interrupt Control Register */ + INT_STAT = 0x4, /* Interrupt Status Register */ + TPU_OFFSET = 0xC, /* Offset operand value register */ + TPU_SYNCHRO = 0xE, /* synchro operand value register */ + IT_DSP_PG = 0x20, +}; + +enum tpu_ctrl_bits { + TPU_CTRL_RESET = (1 << 0), + TPU_CTRL_PAGE = (1 << 1), + TPU_CTRL_EN = (1 << 2), + /* unused */ + TPU_CTRL_DSP_EN = (1 << 4), + /* unused */ + TPU_CTRL_MCU_RAM_ACC = (1 << 6), + TPU_CTRL_TSP_RESET = (1 << 7), + TPU_CTRL_IDLE = (1 << 8), + TPU_CTRL_WAIT = (1 << 9), + TPU_CTRL_CK_ENABLE = (1 << 10), + TPU_CTRL_FULL_WRITE = (1 << 11), +}; + +enum tpu_int_ctrl_bits { + ICTRL_MCU_FRAME = (1 << 0), + ICTRL_MCU_PAGE = (1 << 1), + ICTRL_DSP_FRAME = (1 << 2), + ICTRL_DSP_FRAME_FORCE = (1 << 3), +}; + +#define BIT_SET 1 +#define BIT_CLEAR 0 + +/* wait for a certain control bit to be set */ +static int tpu_wait_ctrl_bit(uint16_t bit, int set) +{ + int timeout = 10*1000; + + while (1) { + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + if (set) { + if (reg & bit) + break; + } else { + if (!(reg & bit)) + break; + } + timeout--; + if (timeout <= 0) { + puts("Timeout while waiting for TPU ctrl bit!\n"); + return -1; + } + } + + return 0; +} + +/* assert or de-assert TPU reset */ +void tpu_reset(int active) +{ + uint16_t reg; + + printd("tpu_reset(%u)\n", active); + reg = readw(TPU_REG(TPU_CTRL)); + if (active) { + reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET); + } else { + reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR); + } +} + +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_enable(%u)\n", active); + if (active) + reg |= TPU_CTRL_EN; + else + reg &= ~TPU_CTRL_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + /* After the new scenario is loaded, TPU switches the MCU-visible memory + * page, i.e. we can write without any danger */ + tpu_rewind(); +#if 0 + { + int i; + uint16_t oldreg = 0; + + for (i = 0; i < 100000; i++) { + reg = readw(TPU_REG(TPU_CTRL)); + if (i == 0 || oldreg != reg) { + printd("%d TPU state: 0x%04x\n", i, reg); + } + oldreg = reg; + } + } +#endif +} + +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_clk_enable(%u)\n", active); + if (active) { + reg |= TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET); + } else { + reg &= ~TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR); + } +} + +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + reg |= TPU_CTRL_DSP_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET); +} + +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + if (reg & TPU_CTRL_DSP_EN) + return 1; + + return 0; +} + +static uint16_t *tpu_ptr; + +void tpu_rewind(void) +{ + dputs("tpu_rewind()\n"); + tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM; +} + +void tpu_enqueue(uint16_t instr) +{ + printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x\n", tpu_ptr, instr); + *tpu_ptr++ = instr; + if (tpu_ptr > (uint16_t *) TPU_RAM_END) + puts("TPU enqueue beyond end of TPU memory\n"); +} + +void tpu_init(void) +{ + /* Get TPU out of reset */ + tpu_reset(1); + tpu_clk_enable(1); + tpu_reset(0); + /* Disable all interrupts */ + writeb(0x7, TPU_REG(INT_CTRL)); + + tpu_rewind(); + tpu_enq_offset(0); + tpu_enq_sync(0); +} + +void tpu_test(void) +{ + int i; + + /* program a sequence of TSPACT events into the TPU */ + for (i = 0; i < 10; i++) { + puts("TSP ACT enable: "); + tsp_act_enable(0x0001); + tpu_enq_wait(10); + puts("TSP ACT disable: "); + tsp_act_disable(0x0001); + tpu_enq_wait(10); + } + tpu_enq_sleep(); + + /* tell the chip to execute the scenario */ + tpu_enable(1); +} + +void tpu_wait_idle(void) +{ + dputs("Waiting for TPU Idle "); + /* Wait until TPU is doing something */ + delay_us(3); + /* Wait until TPU is idle */ + while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE) + dputchar('.'); + dputs("Done!\n"); +} + +void tpu_frame_irq_en(int mcu, int dsp) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + if (mcu) + reg &= ~ICTRL_MCU_FRAME; + else + reg |= ICTRL_MCU_FRAME; + + if (dsp) + reg &= ~ICTRL_DSP_FRAME; + else + reg |= ICTRL_DSP_FRAME; + + writeb(reg, TPU_REG(INT_CTRL)); +} + +void tpu_force_dsp_frame_irq(void) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + reg |= ICTRL_DSP_FRAME_FORCE; + writeb(reg, TPU_REG(INT_CTRL)); +} + +uint16_t tpu_get_offset(void) +{ + return readw(TPU_REG(TPU_OFFSET)); +} + +uint16_t tpu_get_synchro(void) +{ + return readw(TPU_REG(TPU_SYNCHRO)); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(uint16_t a, uint16_t b) +{ + int32_t sum = (uint32_t)a + (uint32_t)b; + + sum %= 5000; + + /* wrap around zero */ + if (sum < 0) + sum += 5000; + + return sum; +} diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c new file mode 100644 index 000000000..5d24f48e9 --- /dev/null +++ b/src/target/firmware/calypso/tsp.c @@ -0,0 +1,121 @@ +/* Calypso DBB internal TSP (Time Serial Port) Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +static uint16_t tspact_state; + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout) +{ + if (bitlen <= 8) { + tpu_enq_move(TPUI_TX_1, dout & 0xff); + } else if (bitlen <= 16) { + tpu_enq_move(TPUI_TX_1, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_2, dout & 0xff); + } else if (bitlen <= 24) { + tpu_enq_move(TPUI_TX_1, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_3, dout & 0xff); + } else { + tpu_enq_move(TPUI_TX_1, (dout >> 24) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_3, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_4, dout & 0xff); + } + tpu_enq_move(TPUI_TSP_CTRL1, (dev_idx << 5) | (bitlen - 1)); + tpu_enq_move(TPUI_TSP_CTRL2, TPUI_CTRL2_WR); +} + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge) +{ + uint8_t reg = TPUI_TSP_SET1 + (dev_idx / 2); + uint8_t val = 0; + uint8_t shift; + + if (dev_idx & 1) + shift = 4; + else + shift = 0; + + if (clk_rising) + val |= 1; + if (en_positive) + val |= 2; + if (en_edge) + val |= 4; + + tpu_enq_move(reg, (val << shift)); +} + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act) +{ + uint8_t low = new_act & 0xff; + uint8_t high = new_act >> 8; + + if (low != (tspact_state & 0xff)) + tpu_enq_move(TPUI_TSP_ACT_L, low); + if (high != (tspact_state >> 8)) + tpu_enq_move(TPUI_TSP_ACT_U, high); + + tspact_state = new_act; +} + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state | bitmask; + tsp_act_update(new_act); +} + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state & ~bitmask; + tsp_act_update(new_act); +} + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void) +{ + return tspact_state; +} + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask) +{ + uint16_t new_act = tspact_state ^ bitmask; + tsp_act_update(new_act); +} + +void tsp_init(void) +{ + tsp_act_update(0); +} diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c new file mode 100644 index 000000000..7e68edc32 --- /dev/null +++ b/src/target/firmware/calypso/uart.c @@ -0,0 +1,396 @@ +/* Calypso DBB internal UART Driver */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by Ingo Albrecht + * + * 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 + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BASE_ADDR_UART_MODEM 0xffff5000 +#define OFFSET_IRDA 0x800 + +#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m)) + +#define LCR7BIT 0x80 +#define LCRBFBIT 0x40 +#define MCR6BIT 0x20 +#define REG_OFFS(m) ((m) &= ~(LCR7BIT|LCRBFBIT|MCR6BIT)) +/* read access LCR[7] = 0 */ +enum uart_reg { + RHR = 0, + IER = 1, + IIR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SPR = 7, + MDR1 = 8, + DMR2 = 9, + SFLSR = 0x0a, + RESUME = 0x0b, + SFREGL = 0x0c, + SFREGH = 0x0d, + BLR = 0x0e, + ACREG = 0x0f, + SCR = 0x10, + SSR = 0x11, + EBLR = 0x12, +/* read access LCR[7] = 1 */ + DLL = RHR | LCR7BIT, + DLH = IER | LCR7BIT, + DIV1_6 = ACREG | LCR7BIT, +/* read/write access LCR[7:0] = 0xbf */ + EFR = IIR | LCRBFBIT, + XON1 = MCR | LCRBFBIT, + XON2 = LSR | LCRBFBIT, + XOFF1 = MSR | LCRBFBIT, + XOFF2 = SPR | LCRBFBIT, +/* read/write access if EFR[4] = 1 and MCR[6] = 1 */ + TCR = MSR | MCR6BIT, + TLR = SPR | MCR6BIT, +}; +/* write access LCR[7] = 0 */ +#define THR RHR +#define FCR IIR /* only if EFR[4] = 1 */ +#define TXFLL SFLSR +#define TXFLH RESUME +#define RXFLL SFREGL +#define RXFLH SFREGH + +enum fcr_bits { + FIFO_EN = (1 << 0), + RX_FIFO_CLEAR = (1 << 1), + TX_FIFO_CLEAR = (1 << 2), + DMA_MODE = (1 << 3), +}; +#define TX_FIFO_TRIG_SHIFT 4 +#define RX_FIFO_TRIG_SHIFT 6 + +enum iir_bits { + IIR_INT_PENDING = 0x01, + IIR_INT_TYPE = 0x3E, + IIR_INT_TYPE_RX_STATUS_ERROR = 0x06, + IIR_INT_TYPE_RX_TIMEOUT = 0x0C, + IIR_INT_TYPE_RHR = 0x04, + IIR_INT_TYPE_THR = 0x02, + IIR_INT_TYPE_MSR = 0x00, + IIR_INT_TYPE_XOFF = 0x10, + IIR_INT_TYPE_FLOW = 0x20, + IIR_FCR0_MIRROR = 0xC0, +}; + +#define UART_REG_UIR 0xffff6000 + +/* enable or disable the divisor latch for access to DLL, DLH */ +static void uart_set_lcr7bit(int uart, int on) +{ + uint8_t reg; + + reg = readb(UART_REG(uart, LCR)); + if (on) + reg |= (1 << 7); + else + reg &= ~(1 << 7); + writeb(reg, UART_REG(uart, LCR)); +} + +static uint8_t old_lcr; +static void uart_set_lcr_bf(int uart, int on) +{ + old_lcr = readb(UART_REG(uart, LCR)); + + if (on) + writeb(0xBF, UART_REG(uart, LCR)); + else + writeb(old_lcr, UART_REG(uart, LCR)); +} + +/* Enable or disable the TCR_TLR latch bit in MCR[6] */ +static void uart_set_mcr6bit(int uart, int on) +{ + uint8_t mcr; + /* we assume EFR[4] is always set to 1 */ + mcr = readb(UART_REG(uart, MCR)); + if (on) + mcr |= (1 << 6); + else + mcr &= ~(1 << 6); + writeb(mcr, UART_REG(uart, MCR)); +} + +static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val) +{ + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + writeb(val, UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); +} + +/* read from a UART register, applying any required latch bits */ +static uint8_t uart_reg_read(int uart, enum uart_reg reg) +{ + uint8_t ret; + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + ret = readb(UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); + + return ret; +} + +static void uart_irq_handler_cons(enum irq_nr irq) +{ + const uint8_t uart = CONS_UART_NR; + uint8_t iir; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RHR: + break; + case IIR_INT_TYPE_THR: + if (cons_rb_flush() == 1) { + /* everything was flushed, disable THR IRQ */ + uint8_t ier = uart_reg_read(uart, IER); + ier &= ~(1 << 1); + uart_reg_write(uart, IER, ier); + } + break; + case IIR_INT_TYPE_MSR: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_RX_TIMEOUT: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static void uart_irq_handler_sercomm(enum irq_nr irq) +{ + const uint8_t uart = SERCOMM_UART_NR; + uint8_t iir, ch; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RX_TIMEOUT: + case IIR_INT_TYPE_RHR: + /* as long as we have rx data available */ + while (uart_getchar_nb(uart, &ch)) { + if (sercomm_drv_rx_char(ch) < 0) { + /* sercomm cannot receive more data right now */ + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0); + } + } + break; + case IIR_INT_TYPE_THR: + /* as long as we have space in the FIFO */ + while (!uart_tx_busy(uart)) { + /* get a byte from sercomm */ + if (!sercomm_drv_pull(&ch)) { + /* no more bytes in sercomm, stop TX interrupts */ + uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0); + break; + } + /* write the byte into the TX FIFO */ + uart_putchar_nb(uart, ch); + } + break; + case IIR_INT_TYPE_MSR: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static const uint8_t uart2irq[] = { + [0] = IRQ_UART_IRDA, + [1] = IRQ_UART_MODEM, +}; + +void uart_init(uint8_t uart) +{ + uint8_t irq = uart2irq[uart]; + + uart_reg_write(uart, IER, 0x00); + if (uart == CONS_UART_NR) { + cons_init(); + irq_register_handler(irq, &uart_irq_handler_cons); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } else { + sercomm_init(); + irq_register_handler(irq, &uart_irq_handler_sercomm); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1); + } +#if 0 + if (uart == 1) { + /* assign UART to MCU and unmask interrupts*/ + writeb(UART_REG_UIR, 0x00); + } +#endif + /* select UART mode */ + uart_reg_write(uart, MDR1, 0); + /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */ + uart_reg_write(uart, EFR, (1 << 4)); + /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */ + uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR | + (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT)); + + /* THR interrupt only when TX FIFO and TX shift register are empty */ + uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3)); + + /* 8 bit, 1 stop bit, no parity, no break */ + uart_reg_write(uart, LCR, 0x03); + + uart_set_lcr7bit(uart, 0); +} + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on) +{ + uint8_t ier = uart_reg_read(uart, IER); + uint8_t mask = 0; + + switch (irq) { + case UART_IRQ_TX_EMPTY: + mask = (1 << 1); + break; + case UART_IRQ_RX_CHAR: + mask = (1 << 0); + break; + } + + if (on) + ier |= mask; + else + ier &= ~mask; + + uart_reg_write(uart, IER, ier); +} + + +void uart_putchar_wait(uint8_t uart, int c) +{ + /* wait while TX FIFO indicates full */ + while (readb(UART_REG(uart, SSR)) & 0x01) { } + + /* put character in TX FIFO */ + writeb(c, UART_REG(uart, THR)); +} + +int uart_putchar_nb(uint8_t uart, int c) +{ + /* if TX FIFO indicates full, abort */ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 0; + + writeb(c, UART_REG(uart, THR)); + return 1; +} + +int uart_getchar_nb(uint8_t uart, uint8_t *ch) +{ + if (!(readb(UART_REG(uart, LSR)) & 0x01)) + return 0; + + *ch = readb(UART_REG(uart, RHR)); + return 1; +} + +int uart_tx_busy(uint8_t uart) +{ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 1; + return 0; +} + +static const uint16_t divider[] = { + [UART_38400] = 21, /* 38,690 */ + [UART_57600] = 14, /* 58,035 */ + [UART_115200] = 7, /* 116,071 */ + [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */ + [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */ + [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */ +}; + +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt) +{ + uint16_t div; + + if (bdrt > ARRAY_SIZE(divider)) + return -1; + + div = divider[bdrt]; + uart_set_lcr7bit(uart, 1); + writeb(div & 0xff, UART_REG(uart, DLL)); + writeb(div >> 8, UART_REG(uart, DLH)); + uart_set_lcr7bit(uart, 0); + + return 0; +} diff --git a/src/target/firmware/comm/Makefile b/src/target/firmware/comm/Makefile new file mode 100644 index 000000000..c2c6fcf01 --- /dev/null +++ b/src/target/firmware/comm/Makefile @@ -0,0 +1,26 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +LIBNAME=comm +CSRCS=msgb.c sercomm.c sercomm_cons.c +SSRCS= + +COBJS=$(CSRCS:.c=.o) +SOBJS=$(SSRCS:.S=.o) +OBJS=$(COBJS) $(SOBJS) + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +$(COBJS): %.o : %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +$(SOBJS): %.o : %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c new file mode 100644 index 000000000..8245ed202 --- /dev/null +++ b/src/target/firmware/comm/msgb.c @@ -0,0 +1,134 @@ +/* (C) 2008-2010 by Harald Welte + * 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 +#include +#include +#include + +#include + +#include + +#include + +#define NO_TALLOC + +void *tall_msgb_ctx; + +#ifdef NO_TALLOC +/* This is a poor mans static allocator for msgb objects */ +#define MSGB_DATA_SIZE 256 +#define MSGB_NUM 16 +struct supermsg { + uint8_t allocated; + struct msgb msg; + uint8_t buf[MSGB_DATA_SIZE]; +}; +static struct supermsg msgs[MSGB_NUM]; +static void *_talloc_zero(void *ctx, unsigned int size, const char *name) +{ + unsigned int i; + if (size > sizeof(struct msgb) + MSGB_DATA_SIZE) + goto panic; + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + if (!msgs[i].allocated) { + msgs[i].allocated = 1; + memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg)); + memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf)); + return &msgs[i].msg; + } + } + +panic: + while (1) { + bl_level(++i % 50); + delay_ms(50); + } + return NULL; +} +static void talloc_free(void *msg) +{ + struct supermsg *smsg = container_of(msg, struct supermsg, msg); + smsg->allocated = 0; +} +#endif + +struct msgb *msgb_alloc(uint16_t size, const char *name) +{ + struct msgb *msg; + + msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); + + if (!msg) { + cons_puts("unable to allocate msgb\n"); + return NULL; + } + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + return msg; +} + +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct msgb, list); +} + +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + /* reset pointers */ + msg->l2h = NULL; + msg->l3h = NULL; +} diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c new file mode 100644 index 000000000..d852eb99c --- /dev/null +++ b/src/target/firmware/comm/sercomm.c @@ -0,0 +1,244 @@ +/* Serial communications layer, based on HDLC */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#ifdef HOST_BUILD +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#endif +#include +#include + +#else +#include +#include + +#include +#include +#include +#endif + +#define SERCOMM_RX_MSG_SIZE 256 + +enum rx_state { + RX_ST_WAIT_START, + RX_ST_ADDR, + RX_ST_CTRL, + RX_ST_DATA, + RX_ST_ESCAPE, +}; + +static struct { + int initialized; + + /* transmit side */ + struct { + struct llist_head dlci_queues[_SC_DLCI_MAX]; + struct msgb *msg; + uint8_t *next_char; + } tx; + + /* receive side */ + struct { + dlci_cb_t dlci_handler[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t dlci; + uint8_t ctrl; + } rx; + +} sercomm; + +void sercomm_init(void) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) + INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]); + + sercomm.rx.msg = NULL; + sercomm.initialized = 1; +} + +int sercomm_initialized(void) +{ + return sercomm.initialized; +} + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg) +{ + uint8_t *hdr; + + /* prepend address + control octet */ + hdr = msgb_push(msg, 2); + hdr[0] = dlci; + hdr[1] = HDLC_C_UI; + msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg); + +#ifndef HOST_BUILD + /* tell UART that we have something to send */ + uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1); +#endif +} + +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci) +{ + struct llist_head *le; + unsigned int num = 0; + + llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) { + num++; + } + + return num; +} + +/* fetch one octet of to-be-transmitted serial data */ +int sercomm_drv_pull(uint8_t *ch) +{ + if (!sercomm.tx.msg) { + unsigned int i; + /* dequeue a new message from the queues */ + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) { + sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]); + if (sercomm.tx.msg) + break; + } + if (sercomm.tx.msg) { + /* start of a new message, send start flag octet */ + *ch = HDLC_FLAG; + sercomm.tx.next_char = sercomm.tx.msg->data; + return 1; + } else { + /* no more data avilable */ + return 0; + } + } + + /* escaping for the two control octets */ + if (*sercomm.tx.next_char == HDLC_FLAG || + *sercomm.tx.next_char == HDLC_ESCAPE) { + /* send an escape octet */ + *ch = HDLC_ESCAPE; + /* invert bit 5 of the next octet to be sent */ + *sercomm.tx.next_char ^= (1 << 5); + } else if (sercomm.tx.next_char == sercomm.tx.msg->tail) { + /* last character has already been transmitted, + * send end-of-message octet */ + *ch = HDLC_FLAG; + /* we've reached the end of the message buffer */ + msgb_free(sercomm.tx.msg); + sercomm.tx.msg = NULL; + sercomm.tx.next_char = NULL; + } else { + /* standard case, simply send next octet */ + *ch = *sercomm.tx.next_char++; + } + return 1; +} + +/* register a handler for a given DLCI */ +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler)) + return -EINVAL; + + if (sercomm.rx.dlci_handler[dlci]) + return -EBUSY; + + sercomm.rx.dlci_handler[dlci] = cb; + return 0; +} + +/* dispatch an incomnig message once it is completely received */ +static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg) +{ + if (sercomm.rx.dlci_handler[dlci]) + sercomm.rx.dlci_handler[dlci](dlci, msg); + else + msgb_free(msg); +} + +/* the driver has received one byte, pass it into sercomm layer */ +int sercomm_drv_rx_char(uint8_t ch) +{ + uint8_t *ptr; + + if (!sercomm.rx.msg) + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + + if (msgb_tailroom(sercomm.rx.msg) == 0) { + msgb_free(sercomm.rx.msg); + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + sercomm.rx.state = RX_ST_WAIT_START; + return 0; + } + + switch (sercomm.rx.state) { + case RX_ST_WAIT_START: + if (ch != HDLC_FLAG) + return 0; + sercomm.rx.state = RX_ST_ADDR; + break; + case RX_ST_ADDR: + sercomm.rx.dlci = ch; + sercomm.rx.state = RX_ST_CTRL; + break; + case RX_ST_CTRL: + sercomm.rx.ctrl = ch; + sercomm.rx.state = RX_ST_DATA; + break; + case RX_ST_DATA: + if (ch == HDLC_ESCAPE) { + /* drop the escape octet, but change state */ + sercomm.rx.state = RX_ST_ESCAPE; + break; + } else if (ch == HDLC_FLAG) { + /* message is finished */ + dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg); + /* allocate new buffer */ + sercomm.rx.msg = NULL; + /* start all over again */ + sercomm.rx.state = RX_ST_WAIT_START; + + /* do not add the control char */ + break; + } + /* default case: store the octet */ + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + break; + case RX_ST_ESCAPE: + /* store bif-5-inverted octet in buffer */ + ch ^= (1 << 5); + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + /* transition back to nromal DATA state */ + sercomm.rx.state = RX_ST_DATA; + break; + } + return 1; +} diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c new file mode 100644 index 000000000..fa08b50ef --- /dev/null +++ b/src/target/firmware/comm/sercomm_cons.c @@ -0,0 +1,126 @@ +/* Serial console layer, layered on top of sercomm HDLC */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include + +#include +#include +#include +#include + +static struct { + struct msgb *cur_msg; +} scons; + +static void raw_puts(const char *s) +{ + int i = strlen(s); + while (i--) + uart_putchar_wait(SERCOMM_UART_NR, *s++); +} + +#ifdef DEBUG +#define raw_putd(x) raw_puts(x) +#else +#define raw_putd(x) +#endif + +int sercomm_puts(const char *s) +{ + const int len = strlen(s) + 1; + unsigned int bytes_left = len; + + if (!sercomm_initialized()) { + raw_putd("sercomm not initialized: "); + raw_puts(s); + return len - 1; + } + + while (bytes_left > 0) { + unsigned int write_num, space_left, flush; + uint8_t *data; + + if (!scons.cur_msg) + scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC); + + if (!scons.cur_msg) { + raw_putd("cannot allocate sercomm msgb: "); + raw_puts(s); + return -ENOMEM; + } + + /* space left in the current msgb */ + space_left = msgb_tailroom(scons.cur_msg); + + if (space_left <= bytes_left) { + write_num = space_left; + /* flush buffer when it is full */ + flush = 1; + } else { + write_num = bytes_left; + flush = 0; + } + + /* obtain pointer where to copy the data */ + data = msgb_put(scons.cur_msg, write_num); + + /* copy data while looking for \n line termination */ + { + unsigned int i; + for (i = 0; i < write_num; i++) { + /* flush buffer at end of line */ + if (*s == '\n') + flush = 1; + *data++ = *s++; + } + } + bytes_left -= write_num; + + if (flush) { + sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg); + /* reset scons.cur_msg pointer to ensure we allocate + * a new one next round */ + scons.cur_msg = NULL; + } + } + + return len - 1; +} + +int sercomm_putchar(int c) +{ + char s[2]; + int rc; + + s[0] = c & 0xff; + s[1] = '\0'; + + rc = sercomm_puts(s); + if (rc < 0) + return rc; + + return c; +} diff --git a/src/target/firmware/display/font_r8x8.c b/src/target/firmware/display/font_r8x8.c new file mode 100644 index 000000000..f6a8a8208 Binary files /dev/null and b/src/target/firmware/display/font_r8x8.c differ diff --git a/src/target/firmware/display/st7558.c b/src/target/firmware/display/st7558.c new file mode 100644 index 000000000..daba94bd7 --- /dev/null +++ b/src/target/firmware/display/st7558.c @@ -0,0 +1,122 @@ +/* Sitronix ST7558 LCD Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#define MORE_CONTROL 0x80 +#define CONTROL_RS_RAM 0x40 +#define CONTROL_RS_CMD 0x00 +#define Y_ADDR(n) (0x40|((n)&0xf)) +#define X_ADDR(n) (0x80|((n)&0x3f)) + +static const uint8_t setup[] = { CONTROL_RS_CMD, 0x2e, 0x21, 0x12, 0xc0, 0x0b, 0x20, 0x11, 0x00, 0x40, 0x80 }; +static const uint8_t home[] = { CONTROL_RS_CMD, Y_ADDR(0), X_ADDR(0) }; + +/* video modes */ +static const uint8_t invert[] = { CONTROL_RS_CMD, 0x20, 0x0d }; +static const uint8_t normal[] = { CONTROL_RS_CMD, 0x20, 0x0c }; +static const uint8_t off[] = { CONTROL_RS_CMD, 0x20, 0x08 }; + +#define ST7558_SLAVE_ADDR 0x3c +static int st7558_write(const uint8_t *data, int len) +{ + int rc = i2c_write(ST7558_SLAVE_ADDR, data[0], 1, data+1, len-1); + /* FIXME: find out why this is needed! */ + delay_ms(10); + return rc; +} + +void st7558_init(void) +{ + /* Release nRESET */ + calypso_reset_set(RESET_EXT, 0); + + i2c_init(0,0); + delay_ms(10); + + st7558_write(setup, sizeof(setup)); + st7558_clrscr(); +} + +void st7558_set_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(invert, sizeof(invert)); +} + +void st7558_unset_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(normal, sizeof(normal)); +} + +static const uint8_t zero16[] = { CONTROL_RS_RAM, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; +void st7558_clrscr(void) +{ + int i; + + st7558_write(home, sizeof(home)); + + for (i = 0; i < 102*9; i += 16) + st7558_write(zero16, sizeof(zero16)); + + st7558_write(home, sizeof(home)); +} + +/* FIXME: we need a mini-libc */ +static void *mcpy(uint8_t *dst, const uint8_t *src, int len) +{ + while (len--) + *dst++ = *src++; + + return dst; +} + +extern const unsigned char fontdata_r8x8[]; + +void st7558_putchar(unsigned char c) +{ + uint8_t putc_buf[16]; + uint8_t bytes_per_char = 8; + + putc_buf[0] = CONTROL_RS_RAM; + mcpy(putc_buf+1, fontdata_r8x8+(c*bytes_per_char), bytes_per_char); + st7558_write(putc_buf, 1+bytes_per_char); +} + +void st7558_puts(const char *str) +{ + char c; + + while ((c = *str++)) + st7558_putchar(c); +} diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c new file mode 100644 index 000000000..faf44cbeb --- /dev/null +++ b/src/target/firmware/flash/cfi_flash.c @@ -0,0 +1,435 @@ +/* NOR Flash Driver for Intel 28F160C3 NOR flash */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include +#include + +/* XXX: memdump_range() */ +#include + +enum flash_cmd { + FLASH_CMD_RESET = 0xff, + FLASH_CMD_READ_ID = 0x90, + FLASH_CMD_CFI = 0x98, + FLASH_CMD_READ_STATUS = 0x70, + FLASH_CMD_CLEAR_STATUS = 0x50, + FLASH_CMD_WRITE = 0x40, + FLASH_CMD_BLOCK_ERASE = 0x20, + FLASH_CMD_ERASE_CONFIRM = 0xD0, + FLASH_CMD_PROTECT = 0x60, +}; + +enum flash_prot_cmd { + FLASH_PROT_LOCK = 0x01, + FLASH_PROT_UNLOCK = 0xD0, + FLASH_PROT_LOCKDOWN = 0x2F +}; + +enum flash_offset { + FLASH_OFFSET_MANUFACTURER_ID = 0x00, + FLASH_OFFSET_DEVICE_ID = 0x01, + FLASH_OFFSET_INTEL_PROTECTION = 0x81, + FLASH_OFFSET_CFI_RESP = 0x10 +}; + +enum flash_block_offset { + FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02 +}; + +enum flash_status { + FLASH_STATUS_READY = 0x80, + FLASH_STATUS_ERASE_SUSPENDED = 0x40, + FLASH_STATUS_ERASE_ERROR = 0x20, + FLASH_STATUS_PROGRAM_ERROR = 0x10, + FLASH_STATUS_VPP_LOW = 0x08, + FLASH_STATUS_PROGRAM_SUSPENDED = 0x04, + FLASH_STATUS_LOCKED_ERROR = 0x02, + FLASH_STATUS_RESERVED = 0x01 +}; + +static inline void flash_write_cmd(const void *base_addr, uint16_t cmd) +{ + writew(cmd, base_addr); +} + +static inline uint16_t flash_read16(const void *base_addr, uint32_t offset) +{ + return readw(base_addr + (offset << 1)); +} + +static char flash_protected(uint32_t block_offset) { + return block_offset < 64*1024; +} + +uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + uint8_t lockstate; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE); + flash_write_cmd(base_addr, FLASH_CMD_RESET); + return lockstate; +} + +void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Unlocking block at 0x%08x\n", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking down block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Erasing block 0x%08x...", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + void *block_addr = ((uint8_t*)base_addr) + block_offset; + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE); + flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_ERASE_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient\n"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected\n"); + } + } else { + puts("done\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) { + const void *base_addr = flash->f_base; + uint32_t i; + + printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src); + + if(dst%2) { + puts("error: unaligned destination\n"); + return; + } + + if(nbytes%2) { + puts("error: unaligned count\n"); + return; + } + + if(flash_protected(dst)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + puts("writing..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + + uint16_t data = *src_addr; + + flash_write_cmd(dst_addr, FLASH_CMD_WRITE); + flash_write_cmd(dst_addr, data); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_PROGRAM_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected"); + } + goto err_reset; + } + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + puts("verifying..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + if(*src_addr != *dst_addr) { + puts("error: verification failed"); + goto err; + } + } + + puts("done\n"); + + return; + + err_reset: + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + err: + printf(" at offset 0x%x\n", i); +} + +typedef void (*flash_block_cb_t)(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size); + +void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry, + uint32_t start_offset, uint32_t end_offset, + flash_block_cb_t callback) +{ + int region, block; + + uint32_t block_start = 0; + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint32_t block_end = block_start + actual_size; + if(block_start >= start_offset && block_end-1 <= end_offset) { + callback(flash, block_start, actual_size); + } + block_start = block_end; + } + } +} + +static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + *manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID); + *device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void get_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i); + *(((unsigned char*)query)+i) = byte; + } + + if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') { + puts("Error: CFI query signature not found\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint8_t byte = *(((uint8_t*)query)+i); + printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_layout(void *base_addr, const struct cfi_query *qry) { + int region; + + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + printf("Region of 0x%04x times 0x%6x bytes\n", actual_count, + actual_size); + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_locks(void *base_addr, const struct cfi_query *qry) { + int region, block; + + uint32_t block_addr = 0; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint8_t lock = flash_read16(base_addr, block_addr+2); + printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock); + block_addr += actual_size / 2; + } + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_protection(void *base_addr) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION); + printf("Protection Lock: 0x%04x\n", lock); + + puts("Protection Data: "); + int i; + for(i = 0; i < 8; i++) { + printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i)); + } + putchar('\n'); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_timing(void *base_addr, struct cfi_query *qry) { + uint32_t block_erase_typ = 1<block_erase_timeout_typ; + uint32_t block_erase_max = (1<block_erase_timeout_max) * block_erase_typ; + uint32_t word_program_typ = 1<word_write_timeout_typ; + uint32_t word_program_max = (1<word_write_timeout_max) * word_program_typ; + printf("Block Erase Typical: %u ms\n", block_erase_typ); + printf("Block Erase Maximum: %u ms\n", block_erase_max); + printf("Word Program Typical: %u us\n", word_program_typ); + printf("Word Program Maximum: %u us\n", word_program_max); +} + +static void dump_algorithms(void *base_addr, struct cfi_query *qry) { + printf("Primary Algorithm ID: %04x\n", qry->p_id); + printf("Primary Extended Query: %04x\n", qry->p_adr); + + printf("Alternate Algorithm ID: %04x\n", qry->a_id); + printf("Alternate Extended Query: %04x\n", qry->a_adr); +} + +void +lockdown_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + flash_block_lockdown(flash, block_offset); +} + +void +print_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + printf("%08x size %08x\n", block_offset, block_size); +} + +void flash_dump_info(cfi_flash_t *flash) { + void *base_addr = flash->f_base; + struct cfi_query *qry = &flash->f_query; + + printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id); + printf("Flash Device ID: %04x\n", flash->f_dev_id); + + printf("Flash Size: 0x%08x bytes\n", flash->f_size); + + dump_algorithms(base_addr, qry); + + dump_timing(base_addr, qry); + + dump_protection(base_addr); + + dump_layout(base_addr, qry); + + dump_locks(base_addr, qry); +} + +void flash_init(cfi_flash_t *flash, void *base_addr) { + printf("Initializing CFI flash at 0x%p\n", base_addr); + + flash->f_base = base_addr; + + get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id); + + get_query(base_addr, &flash->f_query); + + flash->f_size = 1<f_query.dev_size; +} + +void flash_test() { + /* block iterator test */ +#if 0 + flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb); +#endif + + /* programming test */ +#if 0 + static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF}; + + memdump_range(&magic, sizeof(magic)); + +#if 0 +#define ADDR 0x001E0000 + flash_block_unlock(flash, ADDR); + memdump_range(ADDR, 16); + flash_block_erase(flash, ADDR); + memdump_range(ADDR, 16); + flash_program(flash, ADDR, &magic, sizeof(magic)); + memdump_range(ADDR, 16); +#undef ADDR +#endif + +#endif +} diff --git a/src/target/firmware/include/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h new file mode 100644 index 000000000..c6c30aacc --- /dev/null +++ b/src/target/firmware/include/abb/twl3025.h @@ -0,0 +1,128 @@ +#ifndef _TWL3025_H +#define _TWL3025_H + +#define PAGE(n) (n << 7) +enum twl3025_reg { + VRPCCFG = PAGE(1) | 30, + VRPCDEV = PAGE(0) | 30, + VRPCMSK = PAGE(1) | 31, + VRPCMSKABB = PAGE(1) | 29, + VRPCSTS = PAGE(0) | 31, + /* Monitoring ADC Registers */ + MADCTRL = PAGE(0) | 13, + MADCSTAT = PAGE(0) | 24, + VBATREG = PAGE(0) | 15, + VCHGREG = PAGE(0) | 16, + ICHGREG = PAGE(0) | 17, + VBKPREG = PAGE(0) | 18, + ADIN1REG = PAGE(0) | 19, + ADIN2REG = PAGE(0) | 20, + ADIN3REG = PAGE(0) | 21, + ADIN4REG = PAGE(0) | 22, + /* Clock Generator Registers */ + TOGBR1 = PAGE(0) | 4, + TOGBR2 = PAGE(0) | 5, + PWDNRG = PAGE(1) | 9, + TAPCTRL = PAGE(1) | 19, + TAPREG = PAGE(1) | 20, + /* Automatic Frequency Control (AFC) Registers */ + AUXAFC1 = PAGE(0) | 7, + AUXAFC2 = PAGE(0) | 8, + AFCCTLADD = PAGE(1) | 21, + AFCOUT = PAGE(1) | 22, + /* Automatic Power Control (APC) Registers */ + APCDEL1 = PAGE(0) | 2, + APCDEL2 = PAGE(1) | 26, + AUXAPC = PAGE(0) | 9, + APCRAM = PAGE(0) | 10, + APCOFF = PAGE(0) | 11, + APCOUT = PAGE(1) | 12, + /* Auxiliary DAC Control Register */ + AUXDAC = PAGE(0) | 12, + /* SimCard Control Register */ + VRPCSIM = PAGE(1) | 23, + /* LED Driver Register */ + AUXLED = PAGE(1) | 24, + /* Battery Charger Interface (BCI) Registers */ + CHGREG = PAGE(0) | 25, + BCICTL1 = PAGE(0) | 28, + BCICTL2 = PAGE(0) | 29, + BCICONF = PAGE(1) | 13, + /* Interrupt and Bus Control (IBIC) Registers */ + ITMASK = PAGE(0) | 28, + ITSTATREG = PAGE(0) | 27, /* both pages! */ + PAGEREG = PAGE(0) | 1, /* both pages! */ + /* Baseband Codec (BBC) Registers */ + BULIOFF = PAGE(1) | 2, + BULQOFF = PAGE(1) | 3, + BULIDAC = PAGE(1) | 5, + BULQDAC = PAGE(1) | 4, + BULGCAL = PAGE(1) | 14, + BULDATA1 = PAGE(0) | 3, /* 16 words */ + BBCTRL = PAGE(1) | 6, + /* Voiceband Codec (VBC) Registers */ + VBCTRL1 = PAGE(1) | 8, + VBCTRL2 = PAGE(1) | 11, + VBPOP = PAGE(1) | 10, + VBUCTRL = PAGE(1) | 7, + VBDCTRL = PAGE(0) | 6, +}; +#define BULDATA2 BULDATA1 + +enum togbr2_bits { + TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */ + TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */ + TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */ + TOGBR2_ACTS = (1 << 3), /* Activate MCLK */ + TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */ + TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */ + TOGBR2_IAPCPTR = (1 << 6), /* Initialize pointer of APC RAM */ +}; + +enum twl3025_unit { + TWL3025_UNIT_AFC, + TWL3025_UNIT_MAD, + TWL3025_UNIT_ADA, + TWL3025_UNIT_VDL, + TWL3025_UNIT_VUL, +}; + +void twl3025_init(void); +void twl3025_reg_write(uint8_t reg, uint16_t data); +uint16_t twl3025_reg_read(uint8_t reg); + +void twl3025_power_off(void); + +void twl3025_clk13m(int enable); + +void twl3025_unit_enable(enum twl3025_unit unit, int on); + +enum twl3025_tsp_bits { + BULON = 0x80, + BULCAL = 0x40, + BULENA = 0x20, + BDLON = 0x10, + BDLCAL = 0x08, + BDLENA = 0x04, + STARTADC = 0x02, +}; + +/* Enqueue a TSP signal change via the TPU */ +void twl3025_tsp_write(uint8_t data); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at); + +/* Update the AFC DAC value */ +void twl3025_afc_set(int16_t val); + +/* Get the AFC DAC value */ +int16_t twl3025_afc_get(void); + +/* Get the AFC DAC output value */ +uint8_t twl3025_afcout_get(void); + +/* Force a certain static AFC DAC output value */ +void twl3025_afcout_set(uint8_t val); + +#endif diff --git a/src/target/firmware/include/arm.h b/src/target/firmware/include/arm.h new file mode 100644 index 000000000..272c9c39e --- /dev/null +++ b/src/target/firmware/include/arm.h @@ -0,0 +1,7 @@ +#ifndef _ARM_H +#define _ARM_H + +void arm_enable_interrupts(void); +int arm_disable_interrupts(void); + +#endif diff --git a/src/target/firmware/include/asm/assembler.h b/src/target/firmware/include/asm/assembler.h new file mode 100644 index 000000000..b43f9d17d --- /dev/null +++ b/src/target/firmware/include/asm/assembler.h @@ -0,0 +1,97 @@ +/* + * linux/include/asm-arm/assembler.h + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains arm architecture specific defines + * for the different processors. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#include + +#define pull lsl +#define push lsr +#define get_byte_0 lsr #24 +#define get_byte_1 lsr #16 +#define get_byte_2 lsr #8 +#define get_byte_3 lsl #0 +#define put_byte_0 lsl #24 +#define put_byte_1 lsl #16 +#define put_byte_2 lsl #8 +#define put_byte_3 lsl #0 + +#define PLD(code...) + +#define MODE_USR USR_MODE +#define MODE_FIQ FIQ_MODE +#define MODE_IRQ IRQ_MODE +#define MODE_SVC SVC_MODE + +#define DEFAULT_FIQ MODE_FIQ + +/* + * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) + */ +#ifdef __STDC__ +#define LOADREGS(cond, base, reglist...)\ + ldm##cond base,reglist +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist +#endif + +/* + * Build a return instruction for this processor type. + */ +#define RETINSTR(instr, regs...)\ + instr regs + +/* + * Enable and disable interrupts + */ + .macro disable_irq + msr cpsr_c, #PSR_I_BIT | SVC_MODE + .endm + + .macro enable_irq + msr cpsr_c, #SVC_MODE + .endm + +/* + * Save the current IRQ state and disable IRQs. Note that this macro + * assumes FIQs are enabled, and that the processor is in SVC mode. + */ + .macro save_and_disable_irqs, oldcpsr + mrs \oldcpsr, cpsr + disable_irq + .endm + +/* + * Restore interrupt state previously stored in a register. We don't + * guarantee that this will preserve the flags. + */ + .macro restore_irqs, oldcpsr + msr cpsr_c, \oldcpsr + .endm + +/* + * These two are used to save LR/restore PC over a user-based access. + * The old 26-bit architecture requires that we do. On 32-bit + * architecture, we can safely ignore this requirement. + */ + .macro save_lr + .endm + + .macro restore_pc + mov pc, lr + .endm diff --git a/src/target/firmware/include/asm/atomic.h b/src/target/firmware/include/asm/atomic.h new file mode 100644 index 000000000..19e8ce6f9 --- /dev/null +++ b/src/target/firmware/include/asm/atomic.h @@ -0,0 +1,106 @@ +/* + * linux/include/asm-arm/atomic.h + * + * Copyright (C) 1996 Russell King. + * Copyright (C) 2002 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_ATOMIC_H +#define __ASM_ARM_ATOMIC_H + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) + +#include +#include + +#define atomic_set(v,i) (((v)->counter) = (i)) + +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val += i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val -= i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + local_irq_save(flags); + *addr &= ~mask; + local_irq_restore(flags); +} + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int c, old; + + c = atomic_read(v); + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) + c = old; + return c != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#define atomic_add(i, v) (void) atomic_add_return(i, v) +#define atomic_inc(v) (void) atomic_add_return(1, v) +#define atomic_sub(i, v) (void) atomic_sub_return(i, v) +#define atomic_dec(v) (void) atomic_sub_return(1, v) + +#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc_return(v) (atomic_add_return(1, v)) +#define atomic_dec_return(v) (atomic_sub_return(1, v)) +#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) + +#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) + +/* Atomic operations are already serializing on ARM */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff --git a/src/target/firmware/include/asm/bitops.h b/src/target/firmware/include/asm/bitops.h new file mode 100644 index 000000000..337d800d3 --- /dev/null +++ b/src/target/firmware/include/asm/bitops.h @@ -0,0 +1,225 @@ +/* + * Copyright 1995, Russell King. + * Various bits and pieces copyrights include: + * Linus Torvalds (test_bit). + * Big endian support: Copyright 2001, Nicolas Pitre + * reworked by rmk. + * + * bit 0 is the LSB of an "unsigned long" quantity. + * + * Please note that the code in this file should never be included + * from user space. Many of these are not implemented in assembler + * since they would be too costly. Also, they require privileged + * instructions (which are not available from user mode) to ensure + * that they are atomic. + */ + +#ifndef __ASM_ARM_BITOPS_H +#define __ASM_ARM_BITOPS_H + +#include + +#define smp_mb__before_clear_bit() mb() +#define smp_mb__after_clear_bit() mb() + +/* + * These functions are the basis of our bit ops. + * + * First, the atomic bitops. These use native endian. + */ +static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p |= mask; + local_irq_restore(flags); +} + +static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p &= ~mask; + local_irq_restore(flags); +} + +static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p ^= mask; + local_irq_restore(flags); +} + +static inline int +____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res | mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res & ~mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res ^ mask; + local_irq_restore(flags); + + return res & mask; +} + +//#include + +/* + * A note about Endian-ness. + * ------------------------- + * + * When the ARM is put into big endian mode via CR15, the processor + * merely swaps the order of bytes within words, thus: + * + * ------------ physical data bus bits ----------- + * D31 ... D24 D23 ... D16 D15 ... D8 D7 ... D0 + * little byte 3 byte 2 byte 1 byte 0 + * big byte 0 byte 1 byte 2 byte 3 + * + * This means that reading a 32-bit word at address 0 returns the same + * value irrespective of the endian mode bit. + * + * Peripheral devices should be connected with the data bus reversed in + * "Big Endian" mode. ARM Application Note 61 is applicable, and is + * available from http://www.arm.com/. + * + * The following assumes that the data bus connectivity for big endian + * mode has been followed. + * + * Note that bit 0 is defined to be 32-bit word bit 0, not byte 0 bit 0. + */ + +/* + * Little endian assembly bitops. nr = 0 -> byte 0 bit 0. + */ +extern void _set_bit_le(int nr, volatile unsigned long * p); +extern void _clear_bit_le(int nr, volatile unsigned long * p); +extern void _change_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_le(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_le(const void * p, unsigned size); +extern int _find_next_zero_bit_le(const void * p, int size, int offset); +extern int _find_first_bit_le(const unsigned long *p, unsigned size); +extern int _find_next_bit_le(const unsigned long *p, int size, int offset); + +/* + * Big endian assembly bitops. nr = 0 -> byte 3 bit 0. + */ +extern void _set_bit_be(int nr, volatile unsigned long * p); +extern void _clear_bit_be(int nr, volatile unsigned long * p); +extern void _change_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_be(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_be(const void * p, unsigned size); +extern int _find_next_zero_bit_be(const void * p, int size, int offset); +extern int _find_first_bit_be(const unsigned long *p, unsigned size); +extern int _find_next_bit_be(const unsigned long *p, int size, int offset); + +/* + * The __* form of bitops are non-atomic and may be reordered. + */ +#define ATOMIC_BITOP_LE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_le(nr,p)) + +#define ATOMIC_BITOP_BE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_be(nr,p)) + +#define NONATOMIC_BITOP(name,nr,p) \ + (____nonatomic_##name(nr, p)) + +/* + * These are the little endian, atomic definitions. + */ +#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p) +#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p) +#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p) +#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) +#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) +#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) +#define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) +#define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) +#define find_first_bit(p,sz) _find_first_bit_le(p,sz) +#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off) + +#define WORD_BITOFF_TO_LE(x) ((x)) + +#if 0 +#include +#include +#include +#include + +#include + +#include +#include +#endif + +#define BITS_PER_LONG 32 +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif /* _ARM_BITOPS_H */ diff --git a/src/target/firmware/include/asm/ctype.h b/src/target/firmware/include/asm/ctype.h new file mode 100644 index 000000000..afa363922 --- /dev/null +++ b/src/target/firmware/include/asm/ctype.h @@ -0,0 +1,54 @@ +#ifndef _LINUX_CTYPE_H +#define _LINUX_CTYPE_H + +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; + +#define __ismask(x) (_ctype[(int)(unsigned char)(x)]) + +#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) +#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) +#define iscntrl(c) ((__ismask(c)&(_C)) != 0) +#define isdigit(c) ((__ismask(c)&(_D)) != 0) +#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) +#define islower(c) ((__ismask(c)&(_L)) != 0) +#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) +#define ispunct(c) ((__ismask(c)&(_P)) != 0) +#define isspace(c) ((__ismask(c)&(_S)) != 0) +#define isupper(c) ((__ismask(c)&(_U)) != 0) +#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + +#endif diff --git a/src/target/firmware/include/asm/div64.h b/src/target/firmware/include/asm/div64.h new file mode 100644 index 000000000..368261680 --- /dev/null +++ b/src/target/firmware/include/asm/div64.h @@ -0,0 +1,48 @@ +#ifndef __ASM_ARM_DIV64 +#define __ASM_ARM_DIV64 + +#include + +/* + * The semantics of do_div() are: + * + * uint32_t do_div(uint64_t *n, uint32_t base) + * { + * uint32_t remainder = *n % base; + * *n = *n / base; + * return remainder; + * } + * + * In other words, a 64-bit dividend with a 32-bit divisor producing + * a 64-bit result and a 32-bit remainder. To accomplish this optimally + * we call a special __do_div64 helper with completely non standard + * calling convention for arguments and results (beware). + */ + +#ifdef __ARMEB__ +#define __xh "r0" +#define __xl "r1" +#else +#define __xl "r0" +#define __xh "r1" +#endif + +#define do_div(n,base) \ +({ \ + register unsigned int __base asm("r4") = base; \ + register unsigned long long __n asm("r0") = n; \ + register unsigned long long __res asm("r2"); \ + register unsigned int __rem asm(__xh); \ + asm( __asmeq("%0", __xh) \ + __asmeq("%1", "r2") \ + __asmeq("%2", "r0") \ + __asmeq("%3", "r4") \ + "bl __do_div64" \ + : "=r" (__rem), "=r" (__res) \ + : "r" (__n), "r" (__base) \ + : "ip", "lr", "cc"); \ + n = __res; \ + __rem; \ +}) + +#endif diff --git a/src/target/firmware/include/asm/linkage.h b/src/target/firmware/include/asm/linkage.h new file mode 100644 index 000000000..ac1c900f0 --- /dev/null +++ b/src/target/firmware/include/asm/linkage.h @@ -0,0 +1,18 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +/* asm-arm/linkage.h */ + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +/* linux/linkage.h */ + +#define ALIGN __ALIGN + +#define ENTRY(name) \ + .globl name; \ + ALIGN; \ + name: + +#endif diff --git a/src/target/firmware/include/asm/ptrace.h b/src/target/firmware/include/asm/ptrace.h new file mode 100644 index 000000000..f3a654e37 --- /dev/null +++ b/src/target/firmware/include/asm/ptrace.h @@ -0,0 +1,128 @@ +/* + * linux/include/asm-arm/ptrace.h + * + * Copyright (C) 1996-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_PTRACE_H +#define __ASM_ARM_PTRACE_H + +/* + * PSR bits + */ +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 +#define PCMASK 0 + +/* + * Groups of PSR bits + */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * stack during a system call. Note that sizeof(struct pt_regs) + * has to be a multiple of 8. + */ +struct pt_regs { + long uregs[18]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + +#define user_mode(regs) \ + (((regs)->ARM_cpsr & 0xf) == 0) + +#ifdef CONFIG_ARM_THUMB +#define thumb_mode(regs) \ + (((regs)->ARM_cpsr & PSR_T_BIT)) +#else +#define thumb_mode(regs) (0) +#endif + +#define processor_mode(regs) \ + ((regs)->ARM_cpsr & MODE_MASK) + +#define interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_I_BIT)) + +#define fast_interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_F_BIT)) + +#define condition_codes(regs) \ + ((regs)->ARM_cpsr & (PSR_V_BIT|PSR_C_BIT|PSR_Z_BIT|PSR_N_BIT)) + +/* Are the current registers suitable for user mode? + * (used to maintain security in signal handlers) + */ +static inline int valid_user_regs(struct pt_regs *regs) +{ + if (user_mode(regs) && + (regs->ARM_cpsr & (PSR_F_BIT|PSR_I_BIT)) == 0) + return 1; + + /* + * Force CPSR to something logical... + */ + regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT; + + return 0; +} + +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + +#define profile_pc(regs) instruction_pointer(regs) + +#endif /* __ASSEMBLY__ */ + +#endif + diff --git a/src/target/firmware/include/asm/system.h b/src/target/firmware/include/asm/system.h new file mode 100644 index 000000000..2bf0cc511 --- /dev/null +++ b/src/target/firmware/include/asm/system.h @@ -0,0 +1,109 @@ +#ifndef __ASM_ARM_SYSTEM_H +#define __ASM_ARM_SYSTEM_H + +/* Generic ARM7TDMI (ARMv4T) synchronisation primitives, mostly + * taken from Linux kernel source, licensed under GPL */ + +#define local_irq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_save\n" \ +" orr %1, %0, #128\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable IRQs + */ +#define local_irq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_enable\n" \ +" bic %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable IRQs + */ +#define local_irq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_disable\n" \ +" orr %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable FIQs + */ +#define local_fiq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ stf\n" \ +" bic %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable FIQs + */ +#define local_fiq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ clf\n" \ +" orr %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Save the current interrupt enable state. + */ +#define local_save_flags(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_save_flags" \ + : "=r" (x) : : "memory", "cc"); \ + }) + +/* + * restore saved IRQ & FIQ state + */ +#define local_irq_restore(x) \ + __asm__ __volatile__( \ + "msr cpsr_c, %0 @ local_irq_restore\n" \ + : \ + : "r" (x) \ + : "memory", "cc") + +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + (int)(flags & PSR_I_BIT); \ +}) + +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" + +#endif diff --git a/src/target/firmware/include/board.h b/src/target/firmware/include/board.h new file mode 100644 index 000000000..558b636f0 --- /dev/null +++ b/src/target/firmware/include/board.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_H +#define _BOARD_H + +void board_init(void); + +#endif /* _BOARD_H */ diff --git a/src/target/firmware/include/calypso/backlight.h b/src/target/firmware/include/calypso/backlight.h new file mode 100644 index 000000000..3a6abd55a --- /dev/null +++ b/src/target/firmware/include/calypso/backlight.h @@ -0,0 +1,10 @@ +#ifndef _CAL_BACKLIGHT_H +#define _CAL_BACKLIGHT_H + +/* Switch backlight to PWL mode (or back) */ +void bl_mode_pwl(int on); + +/* Set the backlight level */ +void bl_level(uint8_t level); + +#endif /* CAL_BACKLIGHT_H */ diff --git a/src/target/firmware/include/calypso/clock.h b/src/target/firmware/include/calypso/clock.h new file mode 100644 index 000000000..abcfde1d4 --- /dev/null +++ b/src/target/firmware/include/calypso/clock.h @@ -0,0 +1,67 @@ +#ifndef _CALYPSO_CLK_H +#define _CALYPSO_CLK_H + +#include + +#define CALYPSO_PLL26_52_MHZ ((2 << 8) | 0) +#define CALYPSO_PLL26_86_7_MHZ ((10 << 8) | 2) +#define CALYPSO_PLL26_87_MHZ ((3 << 8) | 0) +#define CALYPSO_PLL13_104_MHZ ((8 << 8) | 0) + +enum mclk_div { + _ARM_MCLK_DIV_1 = 0, + ARM_MCLK_DIV_1 = 1, + ARM_MCLK_DIV_2 = 2, + ARM_MCLK_DIV_3 = 3, + ARM_MCLK_DIV_4 = 4, + ARM_MCLK_DIV_5 = 5, + ARM_MCLK_DIV_6 = 6, + ARM_MCLK_DIV_7 = 7, + ARM_MCLK_DIV_1_5 = 0x80 | 1, + ARM_MCLK_DIV_2_5 = 0x80 | 2, +}; + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div); +void calypso_pll_set(uint16_t inp); +void calypso_clk_dump(void); + +/* CNTL_RST */ +enum calypso_rst { + RESET_DSP = (1 << 1), + RESET_EXT = (1 << 2), + RESET_WDOG = (1 << 3), +}; + +void calypso_reset_set(enum calypso_rst calypso_rst, int active); +int calypso_reset_get(enum calypso_rst); + +enum calypso_bank { + CALYPSO_nCS0 = 0, + CALYPSO_nCS1 = 2, + CALYPSO_nCS2 = 4, + CALYPSO_nCS3 = 6, + CALYPSO_nCS7 = 8, + CALYPSO_CS4 = 0xa, + CALYPSO_nCS6 = 0xc, +}; + +enum calypso_mem_width { + CALYPSO_MEM_8bit = 0, + CALYPSO_MEM_16bit = 1, + CALYPSO_MEM_32bit = 2, +}; + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we); + +/* Enable or disable the internal bootrom mapped to 0x0000'0000 */ +void calypso_bootrom(int enable); + +/* Enable or disable the debug unit */ +void calypso_debugunit(int enable); + +/* configure the RHEA bus bridge[s] */ +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1); + +#endif /* _CALYPSO_CLK_H */ diff --git a/src/target/firmware/include/calypso/dma.h b/src/target/firmware/include/calypso/dma.h new file mode 100644 index 000000000..00b9bde76 --- /dev/null +++ b/src/target/firmware/include/calypso/dma.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_DMA_H +#define _CALYPSO_DMA_H + +void dma_init(void); + +#endif /* _CALYPSO_DMA_H */ diff --git a/src/target/firmware/include/calypso/dsp.h b/src/target/firmware/include/calypso/dsp.h new file mode 100644 index 000000000..24779a62a --- /dev/null +++ b/src/target/firmware/include/calypso/dsp.h @@ -0,0 +1,31 @@ +#ifndef _CALYPSO_DSP_H +#define _CALYPSO_DSP_H + +#include + +struct dsp_api { + T_NDB_MCU_DSP *ndb; + T_DB_DSP_TO_MCU *db_r; + T_DB_MCU_TO_DSP *db_w; + T_PARAM_MCU_DSP *param; + int r_page; + int w_page; + int r_page_used; + int frame_ctr; +}; + +extern struct dsp_api dsp_api; + +void dsp_power_on(void); +void dsp_dump_version(void); +void dsp_dump(void); +void dsp_checksum_task(void); +void dsp_api_memset(uint16_t *ptr, int octets); +void dsp_load_afc_dac(uint16_t afc); +void dsp_load_apc_dac(uint16_t apc); +void dsp_end_scenario(void); + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); + +#endif diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h new file mode 100644 index 000000000..5444ec1db --- /dev/null +++ b/src/target/firmware/include/calypso/dsp_api.h @@ -0,0 +1,1554 @@ +#ifndef _CAL_DSP_API_H +#define _CAL_DSP_API_H + +/* This is a header file with structures imported from the TSM30 source code (l1_defty.h) + * + * As this header file only is a list of definitions and data structures, it is + * not ocnsidered to be a copyrightable work itself. + * + * Nonetheless, it might be good to rewrite it (without ugly typedefs!) */ + +#if(L1_DYN_DSP_DWNLD == 1) + #include "l1_dyn_dwl_defty.h" +#endif + +/* Include a header file that defines everything this l1_defty.h needs */ +#include "l1_environment.h" + +#define BASE_API_NDB 0xFFD001A8L /* 268 words */ +#define BASE_API_PARAM 0xFFD00862L /* 57 words */ +#define BASE_API_R_PAGE_0 0xFFD00050L /* 20 words */ +#define BASE_API_R_PAGE_1 0xFFD00078L /* 20 words */ +#define BASE_API_W_PAGE_0 0xFFD00000L /* 20 words */ +#define BASE_API_W_PAGE_1 0xFFD00028L /* 20 words */ + + +/***********************************************************/ +/* */ +/* Data structure for global info components. */ +/* */ +/***********************************************************/ + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only. + // bit [0..7] -> b_fn_report, FN in the normalized reporting period. + // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning. + API d_ctrl_tch; // (9) Tch channel description. + // bit [0..3] -> b_chan_mode, channel mode. + // bit [4..5] -> b_chan_type, channel type. + // bit [6] -> reset SACCH + // bit [7] -> vocoder ON + // bit [8] -> b_sync_tch_ul, synchro. TCH/UL. + // bit [9] -> b_sync_tch_dl, synchro. TCH/DL. + // bit [10] -> b_stop_tch_ul, stop TCH/UL. + // bit [11] -> b_stop_tch_dl, stop TCH/DL. + // bit [12.13] -> b_tch_loop, tch loops A/B/C. + API hole; // (10) unused hole. + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_ctrl_abb; // (11) Bit field indicating the analog baseband register to send. + // bit [0] -> b_ramp: the ramp information(a_ramp[]) is located in NDB + // bit [1.2] -> unused + // bit [3] -> b_apcdel: delays-register in NDB + // bit [4] -> b_afc: freq control register in DB + // bit [5..15] -> unused +#endif + API a_a5fn[2]; // (12..13) Encryption Frame number. + // word 0, bit [0..4] -> T2. + // word 0, bit [5..10] -> T3. + // word 1, bit [0..11] -> T1. + API d_power_ctl; // (14) Power level control. + API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb"). + API d_ctrl_system; // (16) Controle Register for RESET/RESUME. + // bit [0..2] -> b_tsq, training sequence. + // bit [3] -> b_bcch_freq_ind, BCCH frequency indication. + // bit [15] -> b_task_abort, DSP task abort command. +} +T_DB_MCU_TO_DSP; + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) task command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API a_serv_demod[4]; // ( 8..11) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_pm[3]; // (12..14) Power measurement results, array of 3 words. + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#else + API a_pm[3]; // ( 8..10) Power measurement results, array of 3 words. + API a_serv_demod[4]; // (11..14) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#endif +} +T_DB_DSP_TO_MCU; + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 5 words are reserved for any possible mapping modification + API d_hole1_ndb[2]; + #else + // 6 words are reserved for any possible mapping modification + API d_hole1_ndb[3]; + #endif + + #if (AMR == 1) + API p_debug_amr; + #else + API d_hole_debug_amr; + #endif + + #if (CHIPSET == 12) + #if (DSP == 35) || (DSP == 36) + API d_hole2_ndb[1]; + API d_mcsi_select; + #else + API d_hole2_ndb[2]; + #endif + #else + API d_hole2_ndb[2]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + +#if (ANLG_FAM == 3) + // SYREN specific registers + API d_vbpop; + API d_vau_delay_init; + API d_vaud_cfg; + API d_vauo_onoff; + API d_vaus_vol; + API d_vaud_pll; + API d_hole3_ndb[1]; +#elif ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + + API d_hole3_ndb[7]; + +#endif + + // word used for the init of USF threshold + API d_thr_usf_detect; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + // GTT API mapping for DSP code 34 (for test only) + #if (L1_GTT == 1) + API d_tty_status; + API d_tty_detect_thres; + API d_ctm_detect_shift; + API d_tty_fa_thres; + API d_tty_mod_norm; + API d_tty_reset_buffer_ul; + API d_tty_loop_ctrl; + API p_tty_loop_buffer; + #else + API a_tty_holes[8]; + #endif + + API a_sr_holes0[414]; + + #if (L1_NEW_AEC) + // new AEC + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #else + API a_new_aec_holes[12]; + #endif // L1_NEW_AEC + + // Speech recognition model + API a_sr_holes1[145]; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + + API a_cport_holes[1011]; + + API a_model[1041]; + + // EOTD buffer +#if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#else + API a_eotd_holes[22]; +#endif + // AMR ver 1.0 buffers + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; // estimation of the SNR of the AMR speech block + #if (L1_VOICE_MEMO_AMR) + API d_amms_ul_voc; + #else + API a_voice_memo_amr_holes[1]; + #endif + API d_thr_onset_afs; // thresh detection ONSET AFS + API d_thr_sid_first_afs; // thresh detection SID_FIRST AFS + API d_thr_ratscch_afs; // thresh detection RATSCCH AFS + API d_thr_update_afs; // thresh detection SID_UPDATE AFS + API d_thr_onset_ahs; // thresh detection ONSET AHS + API d_thr_sid_ahs; // thresh detection SID frames AHS + API d_thr_ratscch_marker;// thresh detection RATSCCH MARKER + API d_thr_sp_dgr; // thresh detection SPEECH DEGRADED/NO_DATA + API d_thr_soft_bits; + #if (MELODY_E2) + API d_melody_e2_osc_stop; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API d_melody_e2_deltatime; + + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #else + API a_melody_e2_holes0[14]; + #endif + + API a_melody_e2_holes1[693]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #else + API d_holes[61]; + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #endif + #endif + + } + T_NDB_MCU_DSP; +#elif (DSP == 33) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 10 words are reserved for any possible mapping modification + API d_hole1_ndb[5]; + #else + // 11 words are reserved for any possible mapping modification + API d_hole1_ndb[6]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + + API d_hole3_ndb[8]; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + #if (L1_NEW_AEC) + // new AEC + API a_new_aec_holes[422]; + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #endif + + // Speech recognition model + #if (L1_NEW_AEC) + API a_sr_holes[1165]; + #else + API a_sr_holes[1599]; + #endif // L1_NEW_AEC + API a_model[1041]; + + // EOTD buffer + #if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; + #else + API a_eotd_holes[22]; + #endif + + #if (MELODY_E2) + API a_melody_e2_holes0[27]; + API d_melody_e2_osc_used; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API a_melody_e2_holes1[708]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #endif + } + T_NDB_MCU_DSP; + +#elif ((DSP == 32) || (DSP == 31)) + typedef struct + { + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + #if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[9]; + #else // DSP==32 + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif // DSP 32 + + #else // NO MELODY E1 + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #else // DSP==32 + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif //DSP == 32 + #endif // NO MELODY E1 + + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + + API d_bbctrl; + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + +#if (L1_EOTD ==1) + API a_eotd_hole[369]; + + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#endif + #endif + } + T_NDB_MCU_DSP; + + +#else // OTHER DSP CODE like 17 + +typedef struct +{ + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + #if (DATA14_4 == 0) + API a_dd_0[20]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[20]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + #if (DATA14_4 == 0) + API a_du_0[20]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[20]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP == 17) + // selection of the melody format + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #else + API d_melody_selection; + API holes[9]; + #endif + #else // NO MELODY E1 + // selection of the melody format + #if (DSP == 17) + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4] + #else + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #endif + #endif + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + API d_bbctrl; + + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + #if (AEC == 1) + // AEC control.......................(MCU -> DSP). + #if (VOC == FR_EFR) + API p_aec_init; + API p_aec_prog; + API p_spenh_init; + API p_spenh_prog; + #endif + + #if (VOC == FR_HR_EFR) + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + #endif + #endif + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + #endif +} +T_NDB_MCU_DSP; +#endif + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + API_SIGNED d_hole2_param[4]; + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; +#elif (DSP == 33) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + #if DCO_ALGO + API_SIGNED d_cn_dco_param; + + API_SIGNED d_hole2_param[3]; + #else + API_SIGNED d_hole2_param[4]; + #endif + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; + +#else + +typedef struct +{ + //...................................Frequency Burst. + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED hole[1]; + API_SIGNED d_transfer_rate; + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + #if (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + #endif + + //...................................TCH Full Speech. + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + #if (VOC == FR) || (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED d_sd_min_thr_tchefs; //(24L *C_POND_RED) + API_SIGNED d_ma_min_thr_tchefs; //(1200L *C_POND_RED) + API_SIGNED d_md_max_thr_tchefs; //(2000L *C_POND_RED) + API_SIGNED d_md1_max_thr_tchefs; //(160L *C_POND_RED) + API_SIGNED d_hole1; + #endif + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_dsp_test; + + + #if (DATA14_4 == 0 ) || (VOC == FR_HR_EFR) + API_SIGNED d_patch_addr1; + API_SIGNED d_patch_data1; + API_SIGNED d_patch_addr2; + API_SIGNED d_patch_data2; + API_SIGNED d_patch_addr3; + API_SIGNED d_patch_data3; + API_SIGNED d_patch_addr4; + API_SIGNED d_patch_data4; + #endif + + //................................... + API_SIGNED d_version_number; // DSP patch version + API_SIGNED d_ti_version; // customer number. No more used since 1.5 + + API_SIGNED d_dsp_page; + + #if IDS + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + #endif + + +} +T_PARAM_MCU_DSP; +#endif + +#if (DSP_DEBUG_TRACE_ENABLE == 1) +typedef struct +{ + API d_debug_ptr_begin; + API d_debug_ptr_end; +} +T_DB2_DSP_TO_MCU; +#endif + +/* DSP error as per ndb->d_error_status */ +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; + +#endif /* _CAL_DSP_API_H */ diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h new file mode 100644 index 000000000..f2eae0919 --- /dev/null +++ b/src/target/firmware/include/calypso/du.h @@ -0,0 +1,32 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht + * + * 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. + * + */ + +#ifndef _CALYPSO_DU_H +#define _CALYPSO_DU_H + +#include + +void calypso_du_init(); +void calypso_du_stop(); +void calypsu_du_dump(); + +#endif /* _CALYPSO_DU_H */ diff --git a/src/target/firmware/include/calypso/irq.h b/src/target/firmware/include/calypso/irq.h new file mode 100644 index 000000000..5ea597970 --- /dev/null +++ b/src/target/firmware/include/calypso/irq.h @@ -0,0 +1,49 @@ +#ifndef _CALYPSO_IRQ_H +#define _CALYPSO_IRQ_H + +enum irq_nr { + IRQ_WATCHDOG = 0, + IRQ_TIMER1 = 1, + IRQ_TIMER2 = 2, + IRQ_TSP_RX = 3, + IRQ_TPU_FRAME = 4, + IRQ_TPU_PAGE = 5, + IRQ_SIMCARD = 6, + IRQ_UART_MODEM = 7, + IRQ_KEYPAD_GPIO = 8, + IRQ_RTC_TIMER = 9, + IRQ_RTC_ALARM_I2C = 10, + IRQ_ULPD_GAUGING = 11, + IRQ_EXTERNAL = 12, + IRQ_SPI = 13, + IRQ_DMA = 14, + IRQ_API = 15, + IRQ_SIM_DETECT = 16, + IRQ_EXTERNAL_FIQ = 17, + IRQ_UART_IRDA = 18, + IRQ_ULPD_GSM_TIMER = 19, + IRQ_GEA = 20, + _NR_IRQ +}; + +typedef void irq_handler(enum irq_nr nr); + +/* initialize IRQ driver and enable interrupts */ +void irq_init(void); + +/* enable a certain interrupt */ +void irq_enable(enum irq_nr nr); + +/* disable a certain interrupt */ +void irq_disable(enum irq_nr nr); + +/* configure a certain interrupt */ +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio); + +/* register an interrupt handler */ +void irq_register_handler(enum irq_nr nr, irq_handler *handler); + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void); + +#endif /* _CALYPSO_IRQ_H */ diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h new file mode 100644 index 000000000..2d1f8d977 --- /dev/null +++ b/src/target/firmware/include/calypso/l1_environment.h @@ -0,0 +1,365 @@ +#include + +typedef unsigned short API; +typedef signed short API_SIGNED; + +#define FAR + +#define CHIPSET 12 +#define DSP 36 +#define ANLG_FAM 2 /* Iota */ + +/* MFTAB */ +#define L1_MAX_FCT 5 /* Max number of fctions in a frame */ +#define MFTAB_SIZE 20 + +#define NBMAX_CARRIER 174+374 /* Number of carriers (GSM-Ext + DCS */ + +#define DPAGC_FIFO_LEN 4 + +#define SIZE_HIST 10 + +#if !L1_GPRS +# define NBR_DL_L1S_TASKS 32 +#else +# define NBR_DL_L1S_TASKS 45 +#endif + +#define NBR_L1A_PROCESSES 46 + +#define W_A_DSP_IDLE3 1 + + + +// Identifier for all DSP tasks. +// ...RX & TX tasks identifiers. +#define NO_DSP_TASK 0 // No task. +#define NP_DSP_TASK 21 // Normal Paging reading task. +#define EP_DSP_TASK 22 // Extended Paging reading task. +#define NBS_DSP_TASK 19 // Normal BCCH serving reading task. +#define EBS_DSP_TASK 20 // Extended BCCH serving reading task. +#define NBN_DSP_TASK 17 // Normal BCCH neighbour reading task. +#define EBN_DSP_TASK 18 // Extended BCCH neighbour reading task. +#define ALLC_DSP_TASK 24 // CCCH reading task while performing FULL BCCH/CCCH reading task. +#define CB_DSP_TASK 25 // CBCH reading task. +#define DDL_DSP_TASK 26 // SDCCH/D (data) reading task. +#define ADL_DSP_TASK 27 // SDCCH/A (SACCH) reading task. +#define DUL_DSP_TASK 12 // SDCCH/D (data) transmit task. +#define AUL_DSP_TASK 11 // SDCCH/A (SACCH) transmit task. +#define RACH_DSP_TASK 10 // RACH transmit task. +#define TCHT_DSP_TASK 13 // TCH Traffic data DSP task id (RX or TX) +#define TCHA_DSP_TASK 14 // TCH SACCH data DSP task id (RX or TX) +#define TCHD_DSP_TASK 28 // TCH Traffic data DSP task id (RX or TX) + +#define TCH_DTX_UL 15 // Replace UL task in DSP->MCU com. to say "burst not transmitted". + +#if (L1_GPRS) + // Identifier for DSP tasks Packet dedicated. + // ...RX & TX tasks identifiers. + //------------------------------------------------------------------------ + // WARNING ... Need to aligned following macro with MCU/DSP GPRS Interface + //------------------------------------------------------------------------ + #define PNP_DSP_TASK 30 + #define PEP_DSP_TASK 31 + #define PALLC_DSP_TASK 32 + #define PBS_DSP_TASK 33 + + #define PTCCH_DSP_TASK 33 + +#endif + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define FB_DSP_TASK 5 // Freq. Burst reading task in Idle mode. +#define SB_DSP_TASK 6 // Sync. Burst reading task in Idle mode. +#define TCH_FB_DSP_TASK 8 // Freq. Burst reading task in Dedicated mode. +#define TCH_SB_DSP_TASK 9 // Sync. Burst reading task in Dedicated mode. +#define IDLE1 1 + +// Debug tasks +#define CHECKSUM_DSP_TASK 33 +#define TST_NDB 35 // Checksum DSP->MCU +#define TST_DB 36 // DB communication check +#define INIT_VEGA 37 +#define DSP_LOOP_C 38 + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define TCH_LOOP_A 31 +#define TCH_LOOP_B 32 + +// bits in d_gsm_bgd_mgt - background task management +#define B_DSPBGD_RECO 1 // start of reco in dsp background +#define B_DSPBGD_UPD 2 // start of alignement update in dsp background +#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background +#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background + +// bit in d_pll_config +#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration +// **************************************************************** +// NDB AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +// bits in d_tch_mode +#define B_EOTD (1 << 0) // EOTD mode +#define B_PLAY_UL (1 << 3) // Play UL +#define B_DCO_ON (1 << 4) // DCO ON/OFF +#define B_AUDIO_ASYNC (1 << 1) // WCP reserved + +// **************************************************************** +// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +#define C_POND_RED 1L +// below values are defined in the file l1_time.h +//#define D_NSUBB_IDLE 296L +//#define D_NSUBB_DEDIC 30L +#define D_FB_THR_DET_IACQ 0x3333L +#define D_FB_THR_DET_TRACK 0x28f6L +#define D_DC_OFF_THRES 0x7fffL +#define D_DUMMY_THRES 17408L +#define D_DEM_POND_GEWL 26624L +#define D_DEM_POND_RED 20152L +#define D_HOLE 0L +#define D_TRANSFER_RATE 0x6666L + +// Full Rate vocoder definitions. +#define D_MACCTHRESH1 7872L +#define D_MLDT -4L +#define D_MACCTHRESH 7872L +#define D_GU 5772L +#define D_GO 7872L +#define D_ATTMAX 53L +#define D_SM -892L +#define D_B 208L +#define D_SD_MIN_THR_TCHFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHFS 1700L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHFS 99L //(160L *C_POND_RED) + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // Frequency burst definitions + #define D_FB_MARGIN_BEG 24 + #define D_FB_MARGIN_END 22 + + // V42bis definitions + #define D_V42B_SWITCH_HYST 16L + #define D_V42B_SWITCH_MIN 64L + #define D_V42B_SWITCH_MAX 250L + #define D_V42B_RESET_DELAY 10L + + // Latencies definitions + #if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // C.f. BUG1404 + #define D_LAT_MCU_BRIDGE 0x000FL + #else + #define D_LAT_MCU_BRIDGE 0x0009L + #endif + + #define D_LAT_MCU_HOM2SAM 0x000CL + + #define D_LAT_MCU_BEF_FAST_ACCESS 0x0005L + #define D_LAT_DSP_AFTER_SAM 0x0004L + + // Background Task in GSM mode: Initialization. + #define D_GSM_BGD_MGT 0L + +#if (CHIPSET == 4) + #define D_MISC_CONFIG 0L +#elif (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12) + #define D_MISC_CONFIG 1L +#else + #define D_MISC_CONFIG 0L +#endif + +#endif + +// Hall Rate vocoder and ched definitions. + +#define D_SD_MIN_THR_TCHHS 37L +#define D_MA_MIN_THR_TCHHS 344L +#define D_MD_MAX_THR_TCHHS 2175L +#define D_MD1_MAX_THR_TCHHS 138L +#define D_SD_AV_THR_TCHHS 1845L +#define D_WED_FIL_TC 0x7c00L +#define D_WED_FIL_INI 4650L +#define D_X_MIN 15L +#define D_X_MAX 23L +#define D_Y_MIN 703L +#define D_Y_MAX 2460L +#define D_SLOPE 135L +#define D_WED_DIFF_THRESHOLD 406L +#define D_MABFI_MIN_THR_TCHHS 5320L +#define D_LDT_HR -5 +#define D_MACCTRESH_HR 6500 +#define D_MACCTRESH1_HR 6500 +#define D_GU_HR 2620 +#define D_GO_HR 3700 +#define D_B_HR 182 +#define D_SM_HR -1608 +#define D_ATTMAX_HR 53 + +// Enhanced Full Rate vocoder and ched definitions. + +#define C_MLDT_EFR -4 +#define C_MACCTHRESH_EFR 8000 +#define C_MACCTHRESH1_EFR 8000 +#define C_GU_EFR 4522 +#define C_GO_EFR 6500 +#define C_B_EFR 174 +#define C_SM_EFR -878 +#define C_ATTMAX_EFR 53 +#define D_SD_MIN_THR_TCHEFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHEFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHEFS 1230L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHEFS 99L //(160L *C_POND_RED) + + +// Integrated Data Services definitions. +#define D_MAX_OVSPD_UL 8 +// Detect frames containing 90% of 1s as synchro frames +#define D_SYNC_THRES 0x3f50 +// IDLE frames are only frames with 100 % of 1s +#define D_IDLE_THRES 0x4000 +#define D_M1_THRES 5 +#define D_MAX_OVSP_DL 8 + +// d_ra_act: bit field definition +#define B_F48BLK 5 + +// Mask for b_itc information (d_ra_conf) +#define CE_MASK 0x04 + +#define D_FACCH_THR 0 +#define D_DSP_TEST 0 +#define D_VERSION_NUMBER 0 +#define D_TI_VERSION 0 + + +/*------------------------------------------------------------------------------*/ +/* */ +/* DEFINITIONS FOR DSP <-> MCU COMMUNICATION. */ +/* ++++++++++++++++++++++++++++++++++++++++++ */ +/* */ +/*------------------------------------------------------------------------------*/ +// COMMUNICATION Interrupt definition +//------------------------------------ +#define ALL_16BIT 0xffffL +#define B_GSM_PAGE (1 << 0) +#define B_GSM_TASK (1 << 1) +#define B_MISC_PAGE (1 << 2) +#define B_MISC_TASK (1 << 3) + +#define B_GSM_PAGE_MASK (ALL_16BIT ^ B_GSM_PAGE) +#define B_GSM_TASK_MASK (ALL_16BIT ^ B_GSM_TASK) +#define B_MISC_PAGE_MASK (ALL_16BIT ^ B_MISC_PAGE) +#define B_MISC_TASK_MASK (ALL_16BIT ^ B_MISC_TASK) + +// Common definition +//---------------------------------- +// Index to *_DEMOD* arrays. +#define D_TOA 0 // Time Of Arrival. +#define D_PM 1 // Power Measurement. +#define D_ANGLE 2 // Angle (AFC correction) +#define D_SNR 3 // Signal / Noise Ratio. + +// Bit name/position definitions. +#define B_FIRE0 5 // Fire result bit 0. (00 -> NO ERROR) (01 -> ERROR CORRECTED) +#define B_FIRE1 6 // Fire result bit 1. (10 -> ERROR) (11 -> unused) +#define B_SCH_CRC 8 // CRC result for SB decoding. (1 for ERROR). +#define B_BLUD 15 // Uplink,Downlink data block Present. (1 for PRESENT). +#define B_AF 14 // Activity bit: 1 if data block is valid. +#define B_BFI 2 // Bad Frame Indicator +#define B_UFI 0 // UNRELIABLE FRAME Indicator +#define B_ECRC 9 // Enhanced full rate CRC bit +#define B_EMPTY_BLOCK 10 // for voice memo purpose, this bit is used to determine + +#if (DEBUG_DEDIC_TCH_BLOCK_STAT == 1) + #define FACCH_GOOD 10 + #define FACCH_BAD 11 +#endif + +#if (AMR == 1) + // Place of the RX type in the AMR block header + #define RX_TYPE_SHIFT 3 + #define RX_TYPE_MASK 0x0038 + + // Place of the vocoder type in the AMR block header + #define VOCODER_TYPE_SHIFT 0 + #define VOCODER_TYPE_MASK 0x0007 + + // List of the possible RX types in a_dd block + #define SPEECH_GOOD 0 + #define SPEECH_DEGRADED 1 + #define ONSET 2 + #define SPEECH_BAD 3 + #define SID_FIRST 4 + #define SID_UPDATE 5 + #define SID_BAD 6 + #define AMR_NO_DATA 7 + #define AMR_INHIBIT 8 + + // List of possible RX types in RATSCCH block + #define C_RATSCCH_GOOD 5 + + // List of the possible AMR channel rate + #define AMR_CHANNEL_4_75 0 + #define AMR_CHANNEL_5_15 1 + #define AMR_CHANNEL_5_9 2 + #define AMR_CHANNEL_6_7 3 + #define AMR_CHANNEL_7_4 4 + #define AMR_CHANNEL_7_95 5 + #define AMR_CHANNEL_10_2 6 + #define AMR_CHANNEL_12_2 7 + + // Types of RATSCCH blocks + #define C_RATSCCH_UNKNOWN 0 + #define C_RATSCCH_CMI_PHASE_REQ 1 + #define C_RATSCCH_AMR_CONFIG_REQ_MAIN 2 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT 3 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT_IGNORE 4 // Alternative AMR_CONFIG_REQ with updates coming in the next THRES_REQ block + #define C_RATSCCH_THRES_REQ 5 + + // These flags define a bitmap that indicates which AMR parameters are being modified by a RATSCCH + #define C_AMR_CHANGE_CMIP 0 + #define C_AMR_CHANGE_ACS 1 + #define C_AMR_CHANGE_ICM 2 + #define C_AMR_CHANGE_THR1 3 + #define C_AMR_CHANGE_THR2 4 + #define C_AMR_CHANGE_THR3 5 + #define C_AMR_CHANGE_HYST1 6 + #define C_AMR_CHANGE_HYST2 7 + #define C_AMR_CHANGE_HYST3 8 + + // CMIP default value + #define C_AMR_CMIP_DEFAULT 1 // According to ETSI specification 05.09, cmip is always 1 by default (new channel, handover...) + +#endif +// "d_ctrl_tch" bits positions for TCH configuration. +#define B_CHAN_MODE 0 +#define B_CHAN_TYPE 4 +#define B_RESET_SACCH 6 +#define B_VOCODER_ON 7 +#define B_SYNC_TCH_UL 8 +#if (AMR == 1) + #define B_SYNC_AMR 9 +#else +#define B_SYNC_TCH_DL 9 +#endif +#define B_STOP_TCH_UL 10 +#define B_STOP_TCH_DL 11 +#define B_TCH_LOOP 12 +#define B_SUBCHANNEL 15 + +// "d_ctrl_abb" bits positions for conditionnal loading of abb registers. +#define B_RAMP 0 +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + #define B_BULRAMPDEL 3 // Note: this name is changed + #define B_BULRAMPDEL2 2 // Note: this name is changed + #define B_BULRAMPDEL_BIS 9 + #define B_BULRAMPDEL2_BIS 10 +#endif +#define B_AFC 4 + +// "d_ctrl_system" bits positions. +#define B_TSQ 0 +#define B_BCCH_FREQ_IND 3 +#define B_TASK_ABORT 15 // Abort RF tasks for DSP. diff --git a/src/target/firmware/include/calypso/misc.h b/src/target/firmware/include/calypso/misc.h new file mode 100644 index 000000000..3bd81d229 --- /dev/null +++ b/src/target/firmware/include/calypso/misc.h @@ -0,0 +1,7 @@ +#ifndef _CAL_MISC_H + +void memdump_range(unsigned int *ptr, unsigned int len); +void dump_mem(void); +void dump_dev_id(void); + +#endif /* _CAL_MISC_H */ diff --git a/src/target/firmware/include/calypso/rtc.h b/src/target/firmware/include/calypso/rtc.h new file mode 100644 index 000000000..17528d00a --- /dev/null +++ b/src/target/firmware/include/calypso/rtc.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_RTC_H +#define _CALYPSO_RTC_H + +void rtc_init(void); + +#endif /* _CALYPSO_RTC_H */ diff --git a/src/target/firmware/include/calypso/timer.h b/src/target/firmware/include/calypso/timer.h new file mode 100644 index 000000000..96587d5a9 --- /dev/null +++ b/src/target/firmware/include/calypso/timer.h @@ -0,0 +1,22 @@ +#ifndef _CAL_TIMER_H +#define _CAL_TIMER_H + +/* Enable or Disable a timer */ +void hwtimer_enable(int num, int on); + +/* Configure pre-scaler and if timer is auto-reload */ +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload); + +/* Load a timer with the given value */ +void hwtimer_load(int num, uint16_t val); + +/* Read the current timer value */ +uint16_t hwtimer_read(int num); + +/* Enable or disable the watchdog */ +void wdog_enable(int on); + +/* power up the timers */ +void hwtimer_init(void); + +#endif /* _CAL_TIMER_H */ diff --git a/src/target/firmware/include/calypso/tpu.h b/src/target/firmware/include/calypso/tpu.h new file mode 100644 index 000000000..f0226e631 --- /dev/null +++ b/src/target/firmware/include/calypso/tpu.h @@ -0,0 +1,117 @@ +#ifndef _CALYPSO_TPU_H +#define _CALYPSO_TPU_H + +/* Assert or de-assert TPU reset */ +void tpu_reset(int active); +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active); +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active); +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void); +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void); +/* Rewind the TPU, i.e. restart enqueueing instructions at the base addr */ +void tpu_rewind(void); +/* Enqueue a raw TPU instruction */ +void tpu_enqueue(uint16_t instr); +/* Initialize TPU and TPU driver */ +void tpu_init(void); +/* (Busy)Wait until TPU is idle */ +void tpu_wait_idle(void); +/* Enable FRAME interrupt generation */ +void tpu_frame_irq_en(int mcu, int dsp); +/* Force the generation of a DSP interrupt */ +void tpu_force_dsp_frame_irq(void); + +/* Get the current TPU SYNCHRO register */ +uint16_t tpu_get_synchro(void); +/* Get the current TPU OFFSET register */ +uint16_t tpu_get_offset(void); + +enum tpu_instr { + TPU_INSTR_AT = (1 << 13), + TPU_INSTR_OFFSET = (2 << 13), + TPU_INSTR_SYNCHRO = (3 << 13), /* Loading delta synchro value in TPU synchro register */ + TPU_INSTR_WAIT = (5 << 13), /* Wait a certain period (in GSM qbits) */ + TPU_INSTR_SLEEP = (0 << 13), /* Stop the sequencer by disabling TPU ENABLE bit in ctrl reg */ + /* data processing */ + TPU_INSTR_MOVE = (4 << 13), +}; + +/* Addresses internal to the TPU, only accessible via MOVE */ +enum tpu_reg_int { + TPUI_TSP_CTRL1 = 0x00, + TPUI_TSP_CTRL2 = 0x01, + TPUI_TX_1 = 0x04, + TPUI_TX_2 = 0x03, + TPUI_TX_3 = 0x03, + TPUI_TX_4 = 0x05, + TPUI_TSP_ACT_L = 0x06, + TPUI_TSP_ACT_U = 0x07, + TPUI_TSP_SET1 = 0x09, + TPUI_TSP_SET2 = 0x0a, + TPUI_TSP_SET3 = 0x0b, + TPUI_DSP_INT_PG = 0x10, + TPUI_GAUGING_EN = 0x11, +}; + +enum tpui_ctrl2_bits { + TPUI_CTRL2_RD = (1 << 0), + TPUI_CTRL2_WR = (1 << 1), +}; + +static inline uint16_t tpu_mod5000(int16_t time) +{ + if (time < 0) + return time + 5000; + if (time >= 5000) + return time - 5000; + return time; +} + +/* Enqueue a SLEEP operation (stop sequencer by disabling TPU ENABLE bit) */ +static inline void tpu_enq_sleep(void) +{ + tpu_enqueue(TPU_INSTR_SLEEP); +} + +/* Enqueue a MOVE operation */ +static inline void tpu_enq_move(uint8_t addr, uint8_t data) +{ + tpu_enqueue(TPU_INSTR_MOVE | (data << 5) | (addr & 0x1f)); +} + +/* Enqueue an AT operation */ +static inline void tpu_enq_at(int16_t time) +{ + tpu_enqueue(TPU_INSTR_AT | tpu_mod5000(time)); +} + +/* Enqueue a SYNC operation */ +static inline void tpu_enq_sync(int16_t time) +{ + tpu_enqueue(TPU_INSTR_SYNCHRO | time); +} + +/* Enqueue a WAIT operation */ +static inline void tpu_enq_wait(int16_t time) +{ + tpu_enqueue(TPU_INSTR_WAIT | time); +} + +/* Enqueue an OFFSET operation */ +static inline void tpu_enq_offset(int16_t time) +{ + tpu_enqueue(TPU_INSTR_OFFSET | time); +} + +static inline void tpu_enq_dsp_irq(void) +{ + tpu_enq_move(TPUI_DSP_INT_PG, 0x0001); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(uint16_t a, uint16_t b); + +#endif /* _CALYPSO_TPU_H */ diff --git a/src/target/firmware/include/calypso/tsp.h b/src/target/firmware/include/calypso/tsp.h new file mode 100644 index 000000000..0252f36ed --- /dev/null +++ b/src/target/firmware/include/calypso/tsp.h @@ -0,0 +1,30 @@ +#ifndef _CALYPSO_TSP_H +#define _CALYPSO_TSP_H + +#define TSPACT(x) (1 << x) + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout); + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge); + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void); + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act); + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask); + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask); + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask); + +/* Initialize TSP driver */ +void tsp_init(void); + +#endif /* _CALYPSO_TSP_H */ diff --git a/src/target/firmware/include/calypso/uart.h b/src/target/firmware/include/calypso/uart.h new file mode 100644 index 000000000..845612f07 --- /dev/null +++ b/src/target/firmware/include/calypso/uart.h @@ -0,0 +1,30 @@ +#ifndef _CAL_UART_H +#define _CAL_UART_H + +#include + +enum uart_baudrate { + UART_38400, + UART_57600, + UART_115200, + UART_230400, + UART_460800, + UART_614400, + UART_921600, +}; + +void uart_init(uint8_t uart); +void uart_putchar_wait(uint8_t uart, int c); +int uart_putchar_nb(uint8_t uart, int c); +int uart_getchar_nb(uint8_t uart, uint8_t *ch); +int uart_tx_busy(uint8_t uart); +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt); + +enum uart_irq { + UART_IRQ_TX_EMPTY, + UART_IRQ_RX_CHAR, +}; + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on); + +#endif /* _CAL_UART_H */ diff --git a/src/target/firmware/include/cfi_flash.h b/src/target/firmware/include/cfi_flash.h new file mode 100644 index 000000000..2ab8842ab --- /dev/null +++ b/src/target/firmware/include/cfi_flash.h @@ -0,0 +1,68 @@ + +#ifndef _CFI_FLASH_H +#define _CFI_FLASH_H + +#include + + +#define CFI_FLASH_MAX_ERASE_REGIONS 4 + +/* structure of erase region descriptor */ +struct cfi_region { + uint16_t b_count; + uint16_t b_size; +} __attribute__((packed)); + + +/* structure of cfi query response */ +struct cfi_query { + uint8_t qry[3]; + uint16_t p_id; + uint16_t p_adr; + uint16_t a_id; + uint16_t a_adr; + uint8_t vcc_min; + uint8_t vcc_max; + uint8_t vpp_min; + uint8_t vpp_max; + uint8_t word_write_timeout_typ; + uint8_t buf_write_timeout_typ; + uint8_t block_erase_timeout_typ; + uint8_t chip_erase_timeout_typ; + uint8_t word_write_timeout_max; + uint8_t buf_write_timeout_max; + uint8_t block_erase_timeout_max; + uint8_t chip_erase_timeout_max; + uint8_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS]; +} __attribute__((packed)); + +typedef struct { + void *f_base; + + uint32_t f_size; + + uint16_t f_manuf_id; + uint16_t f_dev_id; + + struct cfi_query f_query; +} cfi_flash_t; + +typedef uint8_t flash_lock; + +void flash_init(cfi_flash_t *flash, void *base_addr); + +void flash_dump_info(cfi_flash_t *flash); + +flash_lock flash_block_getlock(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_unlock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lockdown(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_erase(cfi_flash_t *base_addr, uint32_t block_addr); + +#endif diff --git a/src/target/firmware/include/comm/msgb.h b/src/target/firmware/include/comm/msgb.h new file mode 100644 index 000000000..f7c9d1478 --- /dev/null +++ b/src/target/firmware/include/comm/msgb.h @@ -0,0 +1,104 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008-2010 by Harald Welte + * 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 + +struct msgb { + struct llist_head list; + + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; /* start of buffer */ + unsigned char *tail; /* end of message */ + unsigned char *data; /* start of message */ + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->data + msgb->data_len) - msgb->tail; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/target/firmware/include/comm/sercomm.h b/src/target/firmware/include/comm/sercomm.h new file mode 100644 index 000000000..24ad865c6 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm.h @@ -0,0 +1,57 @@ +#ifndef _SERCOMM_H +#define _SERCOMM_H + +/* SERCOMM layer on UART1 (modem UART) */ + +#ifdef HOST_BUILD +#include +#else +#include +#define SERCOMM_UART_NR 1 +#endif + +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D + +#define HDLC_C_UI 0x03 +#define HDLC_C_P_BIT (1 << 4) +#define HDLC_C_F_BIT (1 << 4) + +/* a low sercomm_dlci means high priority. A high DLCI means low priority */ +enum sercomm_dlci { + SC_DLCI_HIGHEST = 0, + SC_DLCI_L1A_L23 = 5, + SC_DLCI_CONSOLE = 10, + _SC_DLCI_MAX +}; + +void sercomm_init(void); +int sercomm_initialized(void); + +/* User Interface: Tx */ + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg); +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci); + +/* User Interface: Rx */ + +/* receiving messages for a given DLCI */ +typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg); +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb); + +/* Driver Interface */ + +/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */ +int sercomm_drv_pull(uint8_t *ch); +/* the driver has received one byte, pass it into sercomm layer. + returns 1 in case of success, 0 in case of unrecognized char */ +int sercomm_drv_rx_char(uint8_t ch); + +static inline struct msgb *sercomm_alloc_msgb(unsigned int len) +{ + return msgb_alloc_headroom(len, 4, "sercomm_tx"); +} + +#endif /* _SERCOMM_H */ diff --git a/src/target/firmware/include/comm/sercomm_cons.h b/src/target/firmware/include/comm/sercomm_cons.h new file mode 100644 index 000000000..11f66545c --- /dev/null +++ b/src/target/firmware/include/comm/sercomm_cons.h @@ -0,0 +1,10 @@ +#ifndef _SERCOMM_CONS_H +#define _SERCOMM_CONS_H + +/* how large buffers do we allocate? */ +#define SERCOMM_CONS_ALLOC 256 + +int sercomm_puts(const char *s); +int sercomm_putchar(int c); + +#endif /* _SERCOMM_CONS_H */ diff --git a/src/target/firmware/include/console.h b/src/target/firmware/include/console.h new file mode 100644 index 000000000..7146e9907 --- /dev/null +++ b/src/target/firmware/include/console.h @@ -0,0 +1,20 @@ +#ifndef _CONSOLE_H +#define _CONSOLE_H + +/* This is the direct (IRQ driven) UART console, bypassing the HDLC layer. + * You should not need to call those functions unless you've decided to + * not use the HLDC layer or have a device with two UARTs */ + +int cons_rb_append(const char *data, int len); +int cons_puts(const char *s); +int cons_putchar(char c); +int cons_rb_flush(void); +void cons_init(void); + +/* We want the console on UART 0 (IRDA UART) */ +#define CONS_UART_NR 0 + +/* Size of the static ring-buffer that we keep for console print messages */ +#define CONS_RB_SIZE 4096 + +#endif /* _CONSOLE_H */ diff --git a/src/target/firmware/include/debug.h b/src/target/firmware/include/debug.h new file mode 100644 index 000000000..27c4185d7 --- /dev/null +++ b/src/target/firmware/include/debug.h @@ -0,0 +1,31 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#ifdef DEBUG +#define dputchar(x) putchar(x) +#define dputs(x) puts(x) +#define dphex(x,y) phex(x,y) +#define printd(x, args ...) printf(x, ## args) +#else +#define dputchar(x) +#define dputs(x) +#define dphex(x,y) +#define printd(x, args ...) +#endif + +#endif /* _DEBUG_H */ diff --git a/src/target/firmware/include/delay.h b/src/target/firmware/include/delay.h new file mode 100644 index 000000000..0d6f3efdf --- /dev/null +++ b/src/target/firmware/include/delay.h @@ -0,0 +1,7 @@ +#ifndef delay_h +#define delay_h + +void delay_ms(unsigned int ms); +void delay_us(unsigned int us); + +#endif diff --git a/src/target/firmware/include/display/st7558.h b/src/target/firmware/include/display/st7558.h new file mode 100644 index 000000000..efed064ca --- /dev/null +++ b/src/target/firmware/include/display/st7558.h @@ -0,0 +1,15 @@ +#ifndef _ST7558_H +#define _ST7558_H + +enum display_attr { + DISP_ATTR_INVERT = 0x0001, +}; + +void st7558_init(void); +void st7558_set_attr(unsigned long attr); +void st7558_unset_attr(unsigned long attr); +void st7558_clrscr(void); +void st7558_putchar(unsigned char c); +void st7558_puts(const char *str); + +#endif diff --git a/src/target/firmware/include/gsm.h b/src/target/firmware/include/gsm.h new file mode 100644 index 000000000..f3250128c --- /dev/null +++ b/src/target/firmware/include/gsm.h @@ -0,0 +1,29 @@ +#ifndef _GSM_H +#define _GSM_H + +#include + +enum gsm_band { + GSM_850 = 1, + GSM_900 = 2, + GSM_1800 = 4, + GSM_1900 = 8, + GSM_450 = 0x10, + GSM_480 = 0x20, + GSM_750 = 0x40, + GSM_810 = 0x80, +}; + +#define ARFCN_PCS 0x8000 + +enum gsm_band gsm_arfcn2band(uint16_t arfcn); + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); + +/* Convert from frame number to GSM time */ +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); + +/* Convert from GSM time to frame number */ +uint32_t gsm_gsmtime2fn(struct gsm_time *time); +#endif diff --git a/src/target/firmware/include/i2c.h b/src/target/firmware/include/i2c.h new file mode 100644 index 000000000..37097a85f --- /dev/null +++ b/src/target/firmware/include/i2c.h @@ -0,0 +1,7 @@ +#ifndef _I2C_H +#define _I2C_H + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len); +void i2c_init(int speed, int slaveadd); + +#endif /* I2C_H */ diff --git a/src/target/firmware/include/keypad.h b/src/target/firmware/include/keypad.h new file mode 100644 index 000000000..dd89734c5 --- /dev/null +++ b/src/target/firmware/include/keypad.h @@ -0,0 +1,66 @@ +#ifndef _KEYPAD_H +#define _KEYPAD_H + +enum buttons { + BTN_0 = 0x00002000, + BTN_1 = 0x00008000, + BTN_2 = 0x00000400, + BTN_3 = 0x00000020, + BTN_4 = 0x00010000, + BTN_5 = 0x00000800, + BTN_6 = 0x00000040, + BTN_7 = 0x00020000, + BTN_8 = 0x00001000, + BTN_9 = 0x00000080, + BTN_STAR = 0x00040000, + BTN_HASH = 0x00000100, + BTN_MENU = 0x00004000, + BTN_LEFT_SB = 0x00080000, + BTN_RIGHT_SB = 0x00000200, + BTN_UP = 0x00000002, + BTN_DOWN = 0x00000004, + BTN_LEFT = 0x00000008, + BTN_RIGHT = 0x00000010, + BTN_OK = 0x00000001, + BTN_POWER = 0x01000000, +}; + +enum key_codes { + KEY_0 = 0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_STAR, //* + KEY_HASH, //# + KEY_MENU, //center of directional keys + KEY_LEFT_SB, //softbutton + KEY_RIGHT_SB, //softbutton + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_OK, //green off-hook + KEY_POWER, //red on-hook + KEY_INV = 0xFF +}; + +enum key_states { + PRESSED, + RELEASED, +}; + +void keypad_init(); + +void keypad_scan(); + +typedef void (*key_handler_t)(enum key_codes code, enum key_states state); + +void keypad_set_handler(key_handler_t handler); + +#endif /* KEYPAD_H */ diff --git a/src/target/firmware/include/layer1/afc.h b/src/target/firmware/include/layer1/afc.h new file mode 100644 index 000000000..2e927a509 --- /dev/null +++ b/src/target/firmware/include/layer1/afc.h @@ -0,0 +1,13 @@ +#ifndef _L1_AFC_H +#define _L1_AFC_H + +/* Input a frequency error sample into the AFC averaging */ +void afc_input(int32_t freq_error, uint16_t arfcn, int valid); + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn); + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void); + +#endif diff --git a/src/target/firmware/include/layer1/agc.h b/src/target/firmware/include/layer1/agc.h new file mode 100644 index 000000000..e4b13f134 --- /dev/null +++ b/src/target/firmware/include/layer1/agc.h @@ -0,0 +1,6 @@ +#ifndef _L1_AGC_H +#define _L1_AGC_H + +int16_t agc_inp_dbm8_by_pm(int16_t pm); + +#endif /* _L1_AGC_H */ diff --git a/src/target/firmware/include/layer1/avg.h b/src/target/firmware/include/layer1/avg.h new file mode 100644 index 000000000..6c5de172d --- /dev/null +++ b/src/target/firmware/include/layer1/avg.h @@ -0,0 +1,23 @@ +#ifndef _L1_AVG_H +#define _L1_AVG_H + +struct running_avg { + /* configuration */ + uint16_t period; /* over how many samples to average */ + uint16_t min_valid; + + int32_t acc_val; + uint16_t num_samples; /* how often did we try to sample? */ + uint16_t num_samples_valid; /* how often did we receive valid samples? */ + + void (*outfn)(struct running_avg *, int32_t avg); + void *priv; +}; + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid); + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg); + +#endif /* _AVG_H */ diff --git a/src/target/firmware/include/layer1/l23_api.h b/src/target/firmware/include/layer1/l23_api.h new file mode 100644 index 000000000..a03c59c87 --- /dev/null +++ b/src/target/firmware/include/layer1/l23_api.h @@ -0,0 +1,11 @@ +#ifndef _L1_L23_API_H +#define _L1_L23_API_H + +#include +#include +#include + +void l1_queue_for_l2(struct msgb *msg); +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr); + +#endif /* _L1_L23_API_H */ diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h new file mode 100644 index 000000000..28eda42c5 --- /dev/null +++ b/src/target/firmware/include/layer1/sync.h @@ -0,0 +1,75 @@ +#ifndef _L1_SYNC_H +#define _L1_SYNC_H + +#include +#include + +struct l1_cell_info { + uint16_t arfcn; + uint32_t bsic; + uint32_t fn_offset; + uint32_t time_alignment; +}; + +struct l1s_state { + struct gsm_time current_time; /* current time */ + struct gsm_time next_time; /* time at next TMDMA irq */ + + struct l1_cell_info serving_cell; + + struct tdma_scheduler tdma_sched; + + uint32_t tpu_offset; + + int task; +}; + +extern struct l1s_state l1s; + +enum l1_sig_num { + L1_SIG_PM, /* Power Measurement */ + L1_SIG_NB, /* Normal Burst */ +}; + +struct l1s_meas_hdr { + uint16_t snr; /* signal/noise ratio */ + int16_t toa_qbit; /* time of arrival (qbits) */ + int16_t pm_dbm8; /* power level in dbm/8 */ + int16_t freq_err; /* Frequency error in Hz */ +}; + +struct l1_signal { + uint16_t signum; + uint16_t arfcn; + union { + struct { + int16_t dbm8[2]; + } pm; + struct { + struct l1s_meas_hdr meas[4]; + uint16_t crc; + uint16_t fire; + uint16_t num_biterr; + uint8_t frame[24]; + } nb; + }; +}; + +typedef void (*l1s_cb_t)(struct l1_signal *sig); + +void l1s_set_handler(l1s_cb_t handler); + +int16_t l1s_snr_int(uint16_t snr); +uint16_t l1s_snr_fract(uint16_t snr); + +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode); +void l1s_sb_test(uint8_t base_fn); +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn); +void l1s_nb_test(uint8_t base_fn); + +void l1s_init(void); + +/* init.c */ +void layer1_init(void); + +#endif /* _L1_SYNC_H */ diff --git a/src/target/firmware/include/layer1/tdma_sched.h b/src/target/firmware/include/layer1/tdma_sched.h new file mode 100644 index 000000000..65c59b83d --- /dev/null +++ b/src/target/firmware/include/layer1/tdma_sched.h @@ -0,0 +1,52 @@ +#ifndef _L1_TDMA_SCHED_H +#define _L1_TDMA_SCHED_H + +#include + +/* TDMA scheduler */ + +/* The idea of this scheduler is that we have a circular buffer of buckets, + * where each bucket corresponds to one future TDMA frame [interrupt]. Each + * bucket contains of a list of callbacks which are executed when the bucket + * index reaches that particular bucket. */ + +#define TDMASCHED_NUM_FRAMES 25 +#define TDMASCHED_NUM_CB 5 + +typedef int tdma_sched_cb(uint16_t p1, uint16_t p2); + +/* A single item in a TDMA scheduler bucket */ +struct tdma_sched_item { + tdma_sched_cb *cb; + uint16_t p1; + uint16_t p2; +}; + +/* A bucket inside the TDMA scheduler */ +struct tdma_sched_bucket { + struct tdma_sched_item item[TDMASCHED_NUM_CB]; + uint8_t num_items; +}; + +/* The scheduler itself, consisting of buckets and a current index */ +struct tdma_scheduler { + struct tdma_sched_bucket bucket[TDMASCHED_NUM_FRAMES]; + uint8_t cur_bucket; +}; + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint16_t p1, uint16_t p2); + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint8_t num_items); + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void); + +/* reset the scheduler; erase all scheduled items */ +void tdma_sched_reset(void); + +/* debug function: print number of entries of all TDMA buckets */ +void tdma_sched_dump(void); + +#endif /* _L1_TDMA_SCHED_H */ diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h new file mode 100644 index 000000000..01fab9100 --- /dev/null +++ b/src/target/firmware/include/layer1/tpu_window.h @@ -0,0 +1,17 @@ +#ifndef _L1_TPU_CTRL_H +#define _L1_TPU_CTRL_H + +enum l1_rxwin_type { + L1_RXWIN_PW, /* power measurement */ + L1_RXWIN_FB, /* FCCH burst detection */ + L1_RXWIN_SB, /* SCH burst detection */ + L1_RXWIN_NB, /* Normal burst decoding */ + _NUM_L1_RXWIN +}; + + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype); + +void tpu_end_scenario(void); + +#endif /* _L1_TPU_CTRL_H */ diff --git a/src/target/firmware/include/linuxlist.h b/src/target/firmware/include/linuxlist.h new file mode 100644 index 000000000..fb99c5ec8 --- /dev/null +++ b/src/target/firmware/include/linuxlist.h @@ -0,0 +1,360 @@ +#ifndef _LINUX_LLIST_H +#define _LINUX_LLIST_H + +#include + +#ifndef inline +#define inline __inline__ +#endif + +static inline void prefetch(const void *x) {;} + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked llist implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct llist_head { + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: llist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/** + * llist_add_tail - add a new entry + * @new: new entry to be added + * @head: llist head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * llist_del - deletes entry from llist. + * @entry: the element to delete from the llist. + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/** + * llist_del_init - deletes entry from llist and reinitialize it. + * @entry: the element to delete from the llist. + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/** + * llist_move - delete from one llist and add as another's head + * @llist: the entry to move + * @head: the head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/** + * llist_move_tail - delete from one llist and add as another's tail + * @llist: the entry to move + * @head: the head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/** + * llist_empty - tests whether a llist is empty + * @head: the llist to test. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * llist_splice - join two llists + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/** + * llist_splice_init - join two llists and reinitialise the emptied llist. + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/** + * llist_entry - get the struct for this entry + * @ptr: the &struct llist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_struct within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * llist_for_each_prev - iterate over a llist backwards + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * llist_for_each_safe - iterate over a llist safe against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * llist_for_each_entry - iterate over llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_reverse - iterate backwards over llist of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * llist_for_each_entry_continue - iterate over llist of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + + +#endif diff --git a/src/target/firmware/include/memory.h b/src/target/firmware/include/memory.h new file mode 100644 index 000000000..b0a0490ce --- /dev/null +++ b/src/target/firmware/include/memory.h @@ -0,0 +1,28 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +#define __arch_getb(a) (*(volatile unsigned char *)(a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) + +#define __raw_writeb(v,a) __arch_putb(v,a) +#define __raw_writew(v,a) __arch_putw(v,a) +#define __raw_writel(v,a) __arch_putl(v,a) + +#define __raw_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(a) + +#define writeb(v,a) __arch_putb(v,a) +#define writew(v,a) __arch_putw(v,a) +#define writel(v,a) __arch_putl(v,a) + +#define readb(a) __arch_getb(a) +#define readw(a) __arch_getw(a) +#define readl(a) __arch_getl(a) + +#endif /* _MEMORY_H */ diff --git a/src/target/firmware/include/rf/trf6151.h b/src/target/firmware/include/rf/trf6151.h new file mode 100644 index 000000000..41cbe6c45 --- /dev/null +++ b/src/target/firmware/include/rf/trf6151.h @@ -0,0 +1,35 @@ +#ifndef _TRF6151_H +#define _TRF6151_H + +#include + +/* initialize (reset + power up) */ +void trf6151_init(void); + +/* switch power off or on */ +void trf6151_power(int on); + +/* set the VGA and RF gain */ +int trf6151_set_gain(uint8_t dbm, int high); + +/* obtain the current total gain of the TRF6151 */ +uint8_t trf6151_get_gain(void); + +/* Request the PLL to be tuned to the given frequency */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink); + +enum trf6151_mode { + TRF6151_IDLE, + TRF6151_RX, + TRF6151_TX, +}; + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode); + +void trf6151_test(uint16_t arfcn); + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn, uint8_t vga_dbm, int rf_gain_high); + +#endif /* TRF6151_H */ diff --git a/src/target/firmware/include/rffe.h b/src/target/firmware/include/rffe.h new file mode 100644 index 000000000..00a270808 --- /dev/null +++ b/src/target/firmware/include/rffe.h @@ -0,0 +1,15 @@ +#ifndef _RFFE_H +#define _RFFE_H + +#include "gsm.h" + +/* initialize RF Frontend */ +void rffe_init(void); + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx); + +/* get current gain of RF frontend (anything between antenna and baseband in dBm */ +uint8_t rffe_get_gain(void); + +#endif diff --git a/src/target/firmware/include/spi.h b/src/target/firmware/include/spi.h new file mode 100644 index 000000000..0925a9a31 --- /dev/null +++ b/src/target/firmware/include/spi.h @@ -0,0 +1,7 @@ +#ifndef _SPI_H +#define _SPI_H + +void spi_init(void); +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din); + +#endif /* _SPI_H */ diff --git a/src/target/firmware/include/stdio.h b/src/target/firmware/include/stdio.h new file mode 100644 index 000000000..c1f86371b --- /dev/null +++ b/src/target/firmware/include/stdio.h @@ -0,0 +1,47 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#include + +int printf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); + +#include + +int vprintf(const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int puts(const char *s); + +#if 0 +/* start.S based uart console */ +#include +#define putchar(c) uart_putchar_wait(1, c) +int puts(const char *s); +#endif + +#if 0 +/* regular UART console */ +#include +#define putchar(c) cons_putchar(c) +#define _puts(s) cons_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +#if 1 +/* sercomm based console */ +#include +#define putchar(c) sercomm_putchar(c) +#define _puts(s) sercomm_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +/* non-standard */ +extern void phex(unsigned int c, unsigned int len); + +#endif /* _STDIO_H */ diff --git a/src/target/firmware/include/string.h b/src/target/firmware/include/string.h new file mode 100644 index 000000000..f060659a0 --- /dev/null +++ b/src/target/firmware/include/string.h @@ -0,0 +1,12 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +size_t strnlen(const char *s, size_t count); +size_t strlen(const char *s); + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); + +#endif diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile new file mode 100644 index 000000000..113e7e60a --- /dev/null +++ b/src/target/firmware/layer1/Makefile @@ -0,0 +1,18 @@ +INCLUDES=-I../include/ -I../../../../include +-include ../Makefile.inc + +LIBNAME=layer1 +OBJS=avg.o agc.o afc.o sync.o gsm.o tdma_sched.o tpu_window.o init.o l23_api.o + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c new file mode 100644 index 000000000..846d208f4 --- /dev/null +++ b/src/target/firmware/layer1/afc.c @@ -0,0 +1,122 @@ +/* AFC (Automatic Frequency Correction) Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include + +#include +#include +#include + +/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */ +#define AFC_PERIOD 40 +/* How many of our measurements have to be valid? */ +#define AFC_MIN_MUN_VALID 8 + +/* The actual AFC code */ + +struct afc_state { + struct running_avg ravg; /* running average */ + int16_t dac_value; /* current DAC output value */ + uint16_t arfcn; +}; + +static void afc_ravg_output(struct running_avg *ravg, int32_t avg); + +static struct afc_state afc_state = { + .ravg = { + .outfn = &afc_ravg_output, + .period = AFC_PERIOD, + .min_valid = AFC_MIN_MUN_VALID, + }, +}; + +/* The AFC DAC in the ABB has to be configured as follows: + * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB) + * where: + * 947 MHz is the center of EGSM + * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies + */ + +#define AFC_NORM_FACTOR_GSM ((1<<15) / 947) +#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894) + +/* we assume 4.9ppb per LSB, equals 0.0049 * 32768 == 160 */ +#define AFC_SLOPE 160 +//#define AFC_SLOPE 141 + +/* The DSP can measure the frequency error in the following ranges: + * FB_MODE0: +/- 20 kHz + * FB_MODE1: +/- 4 kHz + * Sync Burst: +/- 1 kHz + * Normal Burst: +/- 400 Hz + */ + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn) +{ + int32_t afc_norm_factor; + int16_t delta; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_900: + case GSM_850: + afc_norm_factor = AFC_NORM_FACTOR_GSM; + break; + default: + afc_norm_factor = AFC_NORM_FACTOR_DCS; + } + + delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); + printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + afc_state.dac_value += delta; + + /* The AFC DAC has only 13 bits */ + if (afc_state.dac_value > 4095) + afc_state.dac_value = 4095; + else if (afc_state.dac_value < -4096) + afc_state.dac_value = -4096; +} + +void afc_input(int32_t freq_error, uint16_t arfcn, int valid) +{ + afc_state.arfcn = arfcn; + runavg_input(&afc_state.ravg, freq_error, valid); + runavg_check_output(&afc_state.ravg); +} + +/* callback function for runavg */ +static void afc_ravg_output(struct running_avg *ravg, int32_t avg) +{ + afc_correct(avg, afc_state.arfcn); +} + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void) +{ + dsp_api.db_w->d_afc = afc_state.dac_value; + dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC); +} diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c new file mode 100644 index 000000000..aa4af5e19 --- /dev/null +++ b/src/target/firmware/layer1/agc.c @@ -0,0 +1,67 @@ +/* AFC (Automatic Gain Control) Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include + +#include +#include + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* compute the input level present at the antenna based on a baseband + * power measurement of the DSP at baseband */ +int16_t agc_inp_dbm8_by_pm(int16_t pm) +{ + /* pm is in 1/8 dBm at baseband */ + int16_t total_gain_dbm8; + + /* compute total current gain */ + total_gain_dbm8 = (SYSTEM_INHERENT_GAIN + rffe_get_gain()) * 8; + + /* subtract gain from power measurement at baseband level */ + return pm - total_gain_dbm8; +} + +uint8_t agc_il_by_dbm8(int16_t dbm8) +{ + uint16_t il; + + /* convert from 1/8 dBm to l1c format: [220..0] in -1/2dBm unit */ + if (dbm8 >= 0) + il = 0; + else + il = -dbm8; + + /* saturate */ + if (il > 4 * 255) + il = 4 * 255; + + return (uint8_t)(il >> 2); +} diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c new file mode 100644 index 000000000..a4bf565bd --- /dev/null +++ b/src/target/firmware/layer1/avg.c @@ -0,0 +1,57 @@ +/* Averaging Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 + +#include + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid) +{ + ravg->num_samples++; + if (valid) { + ravg->acc_val += val; + ravg->num_samples_valid++; + } +} + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg) +{ + if (ravg->num_samples < ravg->period) + return 0; + + if (ravg->num_samples_valid >= ravg->min_valid) { + int32_t avg = ravg->acc_val / ravg->num_samples_valid; + + ravg->outfn(ravg, avg); + + ravg->num_samples = ravg->num_samples_valid = 0; + ravg->acc_val = 0; + + return 1; + } + + return 0; +} + + diff --git a/src/target/firmware/layer1/gsm.c b/src/target/firmware/layer1/gsm.c new file mode 100644 index 000000000..99f67f30a --- /dev/null +++ b/src/target/firmware/layer1/gsm.c @@ -0,0 +1,117 @@ +/* Generic GSM utility routines */ + +/* (C) 2010 by Harald Welte + * + * 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 + +#include + +enum gsm_band gsm_arfcn2band(uint16_t arfcn) +{ + if (arfcn & ARFCN_PCS) + return GSM_1900; + else if (arfcn <= 124) + return GSM_900; + else if (arfcn >= 955 && arfcn <= 1023) + return GSM_900; + else if (arfcn >= 128 && arfcn <= 251) + return GSM_850; + else if (arfcn >= 512 && arfcn <= 885) + return GSM_1800; + else if (arfcn >= 259 && arfcn <= 293) + return GSM_450; + else if (arfcn >= 306 && arfcn <= 340) + return GSM_480; + else if (arfcn >= 350 && arfcn <= 425) + return GSM_810; + else if (arfcn >= 438 && arfcn <= 511) + return GSM_750; + else + return GSM_1800; +} + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink) +{ + uint16_t freq10_ul; + uint16_t freq10_dl; + + if (arfcn & ARFCN_PCS) { + /* DCS 1900 */ + arfcn &= ~ARFCN_PCS; + freq10_ul = 18502 + 2 * (arfcn-512); + freq10_dl = freq10_ul + 800; + } else if (arfcn <= 124) { + /* Primary GSM + ARFCN 0 of E-GSM */ + freq10_ul = 8900 + 2 * arfcn; + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 955 && arfcn <= 1023) { + /* E-GSM and R-GSM */ + freq10_ul = 8900 + 2 * (arfcn - 1024); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 128 && arfcn <= 251) { + /* GSM 850 */ + freq10_ul = 8242 + 2 * (arfcn - 128); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 512 && arfcn <= 885) { + /* DCS 1800 */ + freq10_ul = 17102 + 2 * (arfcn - 512); + freq10_dl = freq10_ul + 950; + } else if (arfcn >= 259 && arfcn <= 293) { + /* GSM 450 */ + freq10_ul = 4506 + 2 * (arfcn - 259); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 306 && arfcn <= 340) { + /* GSM 480 */ + freq10_ul = 4790 + 2 * (arfcn - 306); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 350 && arfcn <= 425) { + /* GSM 810 */ + freq10_ul = 8060 + 2 * (arfcn - 350); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 438 && arfcn <= 511) { + /* GSM 750 */ + freq10_ul = 7472 + 2 * (arfcn - 438); + freq10_dl = freq10_ul + 300; + } else + return 0xffff; + + if (uplink) + return freq10_ul; + else + return freq10_dl; +} + +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn) +{ + time->fn = fn; + time->t1 = time->fn / (26*51); + time->t2 = time->fn % 26; + time->t3 = time->fn % 51; + time->tc = (time->fn / 51) % 8; +} + +uint32_t gsm_gsmtime2fn(struct gsm_time *time) +{ + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); +} diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c new file mode 100644 index 000000000..1c3877727 --- /dev/null +++ b/src/target/firmware/layer1/init.c @@ -0,0 +1,70 @@ +/* OsmocomBB Layer1 initialization */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void layer1_init(void) +{ + struct msgb *msg; + + /* initialize TDMA Frame IRQ driven synchronous L1 */ + l1s_init(); + /* power up the DSP */ + dsp_power_on(); + + /* Initialize TPU, TSP and TRF drivers */ + tpu_init(); + tsp_init(); + trf6151_init(); + + rffe_init(); + +#if 0 /* only if RX TPU window is disabled! */ + /* Put TWL3025 in downlink mode (includes calibration) */ + twl3025_downlink(1, 1000); +#endif + + /* issue the TRF and TWL initialization sequence */ + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); + + /* Disable RTC interrupt as it causes lost TDMA frames */ + irq_disable(IRQ_RTC_TIMER); + + /* inform l2 and upwards that we are ready for orders */ + msg = l1_create_l2_msg(LAYER1_RESET, 0, 0); + l1_queue_for_l2(msg); +} diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c new file mode 100644 index 000000000..85f73cdef --- /dev/null +++ b/src/target/firmware/layer1/l23_api.c @@ -0,0 +1,63 @@ +/* Synchronous part of GSM Layer 1: API to Layer2+ */ + +/* (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include + +#include +#include + +#include +#include + +/* the size we will allocate struct msgb* for HDLC */ +#define L3_MSG_SIZE (sizeof(struct l1_ccch_info_ind) + 4) +#define L3_MSG_HEAD 4 + +void l1_queue_for_l2(struct msgb *msg) +{ + /* forward via serial for now */ + sercomm_sendmsg(SC_DLCI_L1A_L23, msg); +} + +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr) +{ + struct l1_info_dl *dl; + struct msgb *msg; + + msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1_burst"); + if (!msg) { + while (1) { + puts("OOPS. Out of buffers...\n"); + } + + return NULL; + } + + dl = (struct l1_info_dl *) msgb_put(msg, sizeof(*dl)); + dl->msg_type = msg_type; + /* FIXME: we may want to compute T1/T2/T3 in L23 */ + gsm_fn2gsmtime(&dl->time, fn); + dl->snr[0] = snr; + + return msg; +} diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c new file mode 100644 index 000000000..088b2ea75 --- /dev/null +++ b/src/target/firmware/layer1/sync.c @@ -0,0 +1,911 @@ +/* Synchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by Dieter Spaar + * (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//#define DEBUG_EVERY_TDMA + +/* A debug macro to print every TDMA frame */ +#ifdef DEBUG_EVERY_TDMA +#define putchart(x) putchar(x) +#else +#define putchart(x) +#endif + +struct l1s_state l1s; + +static l1s_cb_t l1s_cb = NULL; + +void l1s_set_handler(l1s_cb_t cb) +{ + l1s_cb = cb; +} + +#define ADD_MODULO(sum, delta, modulo) do { \ + if ((sum += delta) >= modulo) \ + sum -= modulo; \ + } while (0) + +#define GSM_MAX_FN (26*51*2048) + +static void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn) +{ + ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN); + + if (delta_fn == 1) { + ADD_MODULO(time->t2, 1, 26); + ADD_MODULO(time->t3, 1, 51); + + /* if the new frame number is a multiple of 51 */ + if (time->t3 == 0) { + ADD_MODULO(time->tc, 1, 8); + + /* if new FN is multiple of 51 and 26 */ + if (time->t2 == 0) + ADD_MODULO(time->t1, 1, 2048); + } + } else + gsm_fn2gsmtime(time, time->fn); +} + +static void l1s_time_dump(const struct gsm_time *time) +{ + printf("fn=%u(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3); +} + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static int last_task_fnr; + +extern uint16_t rf_arfcn; // TODO + +/* clip a signed 16bit value at a certain limit */ +int16_t clip_int16(int16_t angle, int16_t clip_at) +{ + if (angle > clip_at) + angle = clip_at; + else if (angle < -clip_at) + angle = -clip_at; + + return angle; +} + +int16_t l1s_snr_int(uint16_t snr) +{ + return snr >> 10; +} + +uint16_t l1s_snr_fract(uint16_t snr) +{ + uint32_t fract = snr & 0x3ff; + fract = fract * 1000 / (2 << 10); + + return fract & 0xffff; +} + +static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm) +{ + uint8_t i; + + for (i = 0; i < nbmeas; i++) + pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3); + dsp_api.r_page_used = 1; +} + +/* Convert an angle in fx1.15 notatinon into Hz */ +#define BITFREQ_DIV_2PI 43104 /* 270kHz / 2 * pi */ +#define ANG2FREQ_SCALING (2<<15) /* 2^15 scaling factor for fx1.15 */ +#define ANGLE_TO_FREQ(angle) ((int16_t)angle * BITFREQ_DIV_2PI / ANG2FREQ_SCALING) + +#define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */ +#define AFC_SNR_THRESHOLD 2560 /* 2.5 dB in fx6.10 */ + +#define BITS_PER_TDMA 1250 +#define QBITS_PER_TDMA (BITS_PER_TDMA * 4) /* 5000 */ +#define TPU_RANGE QBITS_PER_TDMA +#define SWITCH_TIME (TPU_RANGE-10) + + +static int fb_once = 0; + +/* synchronize the L1S to a new timebase (typically a new cell */ +static void synchronize_tdma(struct l1_cell_info *cinfo) +{ + int32_t fn_offset; + uint32_t tpu_shift = cinfo->time_alignment; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, + * the next interrupt will basically occur now and we need to compensate */ + if (tpu_shift < SWITCH_TIME) + fn_offset++; + +#if 0 /* probably wrong as we already added "offset" and "shift" above */ + /* increment the TPU quarter-bit offset */ + l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE; +#else + l1s.tpu_offset = tpu_shift; +#endif + + puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +#if 0 + /* FIXME: properly end the TPU window at the emd of l1_sync() */ + tpu_end_scenario(); +#endif + + /* Change the current time to reflect the new value */ + l1s_time_inc(&l1s.current_time, fn_offset); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* The serving cell now no longer has a frame or bit offset */ + cinfo->fn_offset = 0; + cinfo->time_alignment = 0; +} + +static void l1s_reset_hw(void) +{ + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + dsp_api.ndb->d_dsp_page = 0; + + /* we have to really reset the TPU, otherwise FB detection + * somtimes returns wrong TOA values. */ + tpu_reset(1); + tpu_reset(0); + tpu_rewind(); + tpu_enq_wait(5); /* really needed ? */ + tpu_enq_offset(l1s.tpu_offset); + tpu_end_scenario(); +} + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; +}; + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", fb->fnr_report, fb->attempt, + fb->toa, agc_inp_dbm8_by_pm(fb->pm)/8, + ANGLE_TO_FREQ(fb->angle), fb->snr, l1s_snr_int(fb->snr), + l1s_snr_fract(fb->snr), tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz ", fb->fnr_report, fb->attempt, + fb->toa, agc_inp_dbm8_by_pm(fb->pm)/8, + ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static struct mon_state _last_fb, *last_fb = &_last_fb; + +static int read_fb_result(int attempt) +{ + last_fb->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + last_fb->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + last_fb->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + last_fb->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + last_fb->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + last_fb->fnr_report = l1s.current_time.fn; + last_fb->attempt = attempt; + + dump_mon_state(last_fb); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(last_fb->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void read_sb_result(int attempt) +{ + last_fb->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + last_fb->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + last_fb->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + last_fb->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + last_fb->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + last_fb->fnr_report = l1s.current_time.fn; + last_fb->attempt = attempt; + + dump_mon_state(last_fb); + + if (last_fb->snr > AFC_SNR_THRESHOLD) + afc_input(last_fb->freq_diff, rf_arfcn, 1); + else + afc_input(last_fb->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +#define TIMER_TICKS_PER_TDMA 1875 + +static int last_timestamp; + +static inline void check_lost_frame(void) +{ + int diff, timestamp = hwtimer_read(1); + + if (last_timestamp < timestamp) + last_timestamp += (4*TIMER_TICKS_PER_TDMA); + + diff = last_timestamp - timestamp; + if (diff != 1875) + printf("LOST!\n"); + + last_timestamp = timestamp; +} + +/* main routine for synchronous part of layer 1, called by frame interrupt + * generated by TPU once every TDMA frame */ +static void l1_sync(void) +{ + putchart('+'); + + check_lost_frame(); + + /* Increment Time */ + l1s.current_time = l1s.next_time; + l1s_time_inc(&l1s.next_time, 1); + //l1s_time_dump(&l1s.current_time); putchar(' '); + + dsp_api.frame_ctr++; + dsp_api.r_page_used = 0; + + /* Update pointers */ + if (dsp_api.w_page == 0) + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + else + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1; + + if (dsp_api.r_page == 0) + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + else + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1; + + /* Reset MCU->DSP page */ + dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w)); + + /* Update AFC */ + afc_load_dsp(); + + if (dsp_api.ndb->d_error_status) { + printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status); + dsp_api.ndb->d_error_status = 0; + } + +#if 0 + if (l1s.task != dsp_api.db_r->d_task_md) + printf("DSP task (%u) and L1S task (%u) disagree\n", dsp_api.db_r->d_task_md, l1s.task); +#endif + /* execute the sched_items that have been scheduled for this TDMA frame */ + tdma_sched_execute(); + + if (dsp_api.r_page_used) { + /* clear and switch the read page */ + dsp_api_memset((uint16_t *) dsp_api.db_r, sizeof(*dsp_api.db_r)); + + /* TSM30 does it (really needed ?): + * Set crc result as "SB not found". */ + dsp_api.db_r->a_sch[0] = (1<d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */ + dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */ + dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */ + dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */ + dsp_api.ndb->d_dsp_page = 0; + + /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */ + dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT); + return 0; +} + +void l1s_dsp_abort(void) +{ + /* abort right now */ + tdma_schedule(0, &l1s_abort_cmd, 0, 0); +} + +/* FCCH Burst *****************************************************************/ + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(uint16_t p1, uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + /* Program DSP */ + l1s.task = dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + dsp_end_scenario(); + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_FB); + tpu_end_scenario(); + + return 0; +} + + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(uint16_t p1, uint16_t attempt) +{ + int ntdma, qbits, fn_offset; + + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB, fall back to mode 0! */ + if (attempt == 12) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* if we are already synchronized initially */ + if (fb_once == 1) + l1s_fb_test(1, 1); + else + l1s_fb_test(1, 0); + } + return 0; + } + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(attempt); + + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + { + fn_offset = l1s.current_time.fn - attempt + ntdma; + int fnr_delta = last_fb->fnr_report - attempt; + int bits_delta = fnr_delta * BITS_PER_TDMA; + + struct l1_cell_info *cinfo = &l1s.serving_cell; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in the future?!?\n", + last_fb->toa - bits_delta); + else { + int fb_fnr = last_task_fnr + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", fb_fnr, fn_offset, qbits); + } + } + + if (dsp_api.frame_ctr > 500 && fb_once == 0) { + /* Don't synchronize_tdma() yet, it does probably not work + * reliable due to the TPU reset) */ + l1s_reset_hw(); + tdma_sched_reset(); + fb_once = 1; + } else { + /* We found a frequency burst, reset everything and start next task */ + l1s_reset_hw(); + tdma_sched_reset(); + } + +#if 1 + /* restart a SB or new FB detection task */ + if (dsp_api.frame_ctr > 1000 && fb_once == 1 && + abs(last_fb->freq_diff) < 1000) { + int delay; + + /* synchronize before reading SB */ + synchronize_tdma(&l1s.serving_cell); + + delay = fn_offset + 11 - l1s.current_time.fn - 1; + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + fb_once = 0; + l1s_sb_test(delay); + } else +#endif + { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* use FB_MODE_1 if we are within certain limits */ + if (abs(last_fb->freq_diff < 2000)) + l1s_fb_test(fn_offset + 10 - l1s.current_time.fn - 1, 1); + else + l1s_fb_test(fn_offset + 10 - l1s.current_time.fn - 1, 0); + } + + return 0; +} + +#define SCHED_ITEM(x, y, z) { .cb = x, .p1 = y, .p2 = z } +#define SCHED_END_FRAME() { .cb = NULL, .p1 = 0, .p2 = 0 } + +/* we don't really use this because we need to configure the fb_mode! */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM(l1s_fbdet_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 12), SCHED_END_FRAME(), +}; + +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode) +{ +#if 1 + int i; + /* schedule the FB detection command */ + tdma_schedule(base_fn, &l1s_fbdet_cmd, 0, fb_mode); + + /* schedule 12 attempts to read the result */ + for (i = 1; i <= 12; i++) { + uint8_t fn = base_fn + 1 + i; + tdma_schedule(fn, &l1s_fbdet_resp, 0, i); + } +#else + /* use the new scheduler 'set' and simply schedule the whole set */ + /* WARNING: we cannot set FB_MODE_1 this way !!! */ + tdma_schedule_set(base_fn, fb_sched_set, ARRAY_SIZE(fb_sched_set)); +#endif +} + +/* SCH Burst Detection ********************************************************/ + +static int sb_once = 0; + +static uint8_t sb_cnt; + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(uint16_t p1, uint16_t attempt) +{ + uint32_t sb; + uint8_t bsic; + static struct gsm_time sb_time; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + struct l1_sync_new_ccch_resp *l1; + struct msgb *msg; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + bsic = l1s_decode_sb(&sb_time, sb); + printf("=> SB 0x%08x: BSIC=%u ", sb, bsic); + l1s_time_dump(&sb_time); + + l1s.serving_cell.bsic = bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the future?!?\n", + last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + if (sb_cnt > 5 && sb_once == 0) { + synchronize_tdma(&l1s.serving_cell); + sb_once = 1; + } + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, sb_time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* place it in the queue for the layer2 */ + msg = l1_create_l2_msg(SYNC_NEW_CCCH_RESP, sb_time.fn, last_fb->snr); + l1 = (struct l1_sync_new_ccch_resp *) msgb_put(msg, sizeof(*l1)); + l1->bsic = bsic; + l1_queue_for_l2(msg); + +#if 0 + tdma_sched_reset(); +#else + /* + If we call tdma_sched_reset(), which is only needed if there are + further l1s_sbdet_resp() scheduled, we will bring dsp_api.db_r and + dsp_api.db_w out of sync because we changed dsp_api.db_w for l1s_sbdet_cmd() + and canceled l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + however expects dsp_api.db_w and dsp_api.db_r to be in sync (either + "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w and dsp_api.db_r + into sync again, otherwise NB reading will complain. We probably don't + need the Abort command and could just bring dsp_api.db_w and dsp_api.db_r + into sync. + */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } +#endif + if (sb_cnt > 10 && sb_time.t3 == 41) { + l1s_reset_hw(); + /* current t3 == 43, need to start NB detection in t3 = 1, difference is 9 */ + l1s_nb_test(9); + } else { + /* We have just seen a SCH burst, we know the next one is not in + * less than 7 TDMA frames from now */ + l1s_sb_test(7); + } + + return 0; +} + +static int l1s_sbdet_cmd(uint16_t p1, uint16_t p2) +{ + putchart('S'); + + l1s.task = dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB); + tpu_end_scenario(); + + return 0; +} + +void l1s_sb_test(uint8_t base_fn) +{ +#if 1 + /* This is how it is done by the TSM30 */ + tdma_schedule(base_fn, &l1s_sbdet_cmd, 0, 1); + tdma_schedule(base_fn + 1, &l1s_sbdet_cmd, 0, 2); + tdma_schedule(base_fn + 3, &l1s_sbdet_resp, 0, 1); + tdma_schedule(base_fn + 4, &l1s_sbdet_resp, 0, 2); +#else + tdma_schedule(base_fn, &l1s_sbdet_cmd, 0, 1); + tdma_schedule(base_fn + 1, &l1s_sbdet_resp, 0, 1); + tdma_schedule(base_fn + 2, &l1s_sbdet_resp, 0, 2); +#endif +} + +/* Power Measurement **********************************************************/ + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_pm_cmd(uint16_t p1, uint16_t arfcn) +{ + putchart('P'); + + l1s.task = dsp_api.db_w->d_task_md = 2; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + //l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW); + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB); + tpu_end_scenario(); + + return 0; +} + +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_pm_resp(uint16_t p1, uint16_t p2) +{ + uint16_t pm_level[2]; + struct l1_signal sig; + + putchart('p'); + + l1ddsp_meas_read(2, pm_level); + + printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n", + agc_inp_dbm8_by_pm(pm_level[0])/8, + agc_inp_dbm8_by_pm(pm_level[1])/8, rf_arfcn); + + /* build and deliver signal */ + sig.signum = L1_SIG_PM; + sig.arfcn = rf_arfcn; + sig.pm.dbm8[0] = agc_inp_dbm8_by_pm(pm_level[0]); + sig.pm.dbm8[1] = agc_inp_dbm8_by_pm(pm_level[1]); + + if (l1s_cb) + l1s_cb(&sig); + + return 0; +} + +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) +{ + tdma_schedule(base_fn, &l1s_pm_cmd, 0, arfcn); + tdma_schedule(base_fn + 2, &l1s_pm_resp, 0, 0); +} + +/* Normal Burst ***************************************************************/ + +static int l1s_nb_resp(uint16_t p1, uint16_t burst_id) +{ + static struct l1_signal _nb_sig, *sig = &_nb_sig; + struct msgb *msg; + + putchart('n'); + + /* just for debugging, d_task_d should not be 0 */ + if (dsp_api.db_r->d_task_d == 0) { + puts("EMPTY\n"); + return 0; + } + + /* DSP burst ID needs to corespond with what we expect */ + if (dsp_api.db_r->d_burst_d != burst_id) { + puts("BURST ID\n"); + return 0; + } + + sig->nb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + sig->nb.meas[burst_id].pm_dbm8 = dsp_api.db_r->a_serv_demod[D_PM] >> 3; + sig->nb.meas[burst_id].freq_err = ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + sig->nb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (sig->nb.meas[burst_id].snr > AFC_SNR_THRESHOLD) + afc_input(sig->nb.meas[burst_id].freq_err, rf_arfcn, 1); + else + afc_input(sig->nb.meas[burst_id].freq_err, rf_arfcn, 0); + + /* 4th burst, get frame data */ + if (dsp_api.db_r->d_burst_d == 3) { + struct l1_info_dl *dl; + struct l1_ccch_info_ind *l1; + uint8_t i, j; + + sig->signum = L1_SIG_NB; + sig->nb.num_biterr = dsp_api.ndb->a_cd[2] & 0xffff; + sig->nb.crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + sig->nb.fire = ((dsp_api.ndb->a_cd[0] & 0xffff) & (1 << B_FIRE1)) >> B_FIRE1; + + /* copy actual data, skipping the information block [0,1,2] */ + for (j = 0,i = 3; i < 15; i++) { + sig->nb.frame[j++] = dsp_api.ndb->a_cd[i] & 0xFF; + sig->nb.frame[j++] = (dsp_api.ndb->a_cd[i] >> 8) & 0xFF; + } + + /* actually issue the signal */ + if (l1s_cb) + l1s_cb(sig); + + /* place it in the queue for the layer2 */ + msg = l1_create_l2_msg(CCCH_INFO_IND, l1s.current_time.fn-4, last_fb->snr); + dl = (struct l1_info_dl *) msg->data; + l1 = (struct l1_ccch_info_ind *) msgb_put(msg, sizeof(*l1)); + + /* copy the snr and data */ + for (i = 0; i < 3; ++i) + dl->snr[i] = sig->nb.meas[i].snr; + for (i = 0; i < 23; ++i) + l1->data[i] = sig->nb.frame[i]; + l1_queue_for_l2(msg); + + /* clear downlink task */ + l1s.task = dsp_api.db_w->d_task_d = 0; + + l1s_sb_test(4); + } + + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_nb_cmd(uint16_t p1, uint16_t burst_id) +{ + uint8_t tsc = l1s.serving_cell.bsic & 0x7; + + putchart('N'); + dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc); + dsp_end_scenario(); + + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_NB); + tpu_end_scenario(); + + return 0; +} + +static const struct tdma_sched_item nb_sched_set[] = { + SCHED_ITEM(l1s_nb_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_cmd, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 0), SCHED_ITEM(l1s_nb_cmd, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 1), SCHED_ITEM(l1s_nb_cmd, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 3), SCHED_END_FRAME(), +}; + +void l1s_nb_test(uint8_t base_fn) +{ + puts("Starting NB\n"); + tdma_schedule_set(base_fn, nb_sched_set, ARRAY_SIZE(nb_sched_set)); +} + +/* Interrupt handler */ +static void frame_irq(enum irq_nr nr) +{ + l1_sync(); +} + +void l1s_init(void) +{ + /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */ + irq_register_handler(IRQ_TPU_FRAME, &frame_irq); + irq_config(IRQ_TPU_FRAME, 1, 1, 0); + irq_enable(IRQ_TPU_FRAME); + + /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */ + hwtimer_enable(1, 1); + hwtimer_load(1, (1875*4)-1); + hwtimer_config(1, 0, 1); + hwtimer_enable(1, 1); +} + diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c new file mode 100644 index 000000000..097e9a426 --- /dev/null +++ b/src/target/firmware/layer1/tdma_sched.c @@ -0,0 +1,163 @@ +/* TDMA Scheduler Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include + +#include +#include + +#include + +static uint8_t wrap_bucket(uint8_t offset) +{ + uint16_t bucket; + + bucket = (l1s.tdma_sched.cur_bucket + offset) + % ARRAY_SIZE(l1s.tdma_sched.bucket); + + return bucket; +} + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint16_t p1, uint16_t p2) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + struct tdma_sched_item *sched_item; + + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + + sched_item = &bucket->item[bucket->num_items++]; + + sched_item->cb = cb; + sched_item->p1 = p1; + sched_item->p2 = p2; + + return 0; +} + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, + uint8_t num_items) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + int i; + + for (i = 0; i < num_items; i++) { + const struct tdma_sched_item *sched_item = &item_set[i]; + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + + if (sched_item->cb == NULL) { + /* advance to next bucket (== TDMA frame) */ + bucket_nr = wrap_bucket(++frame_offset); + continue; + } + /* check for bucket overflow */ + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + /* copy the item from the set into the current bucket item position */ + memcpy(&bucket->item[bucket->num_items++], sched_item, sizeof(*sched_item)); + } + + return num_items; +} + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + struct tdma_sched_bucket *bucket; + uint8_t next_bucket; + int i, num_events = 0; + + /* determine current bucket */ + bucket = &sched->bucket[sched->cur_bucket]; + + /* iterate over items in this bucket and call callback function */ + for (i = 0; i < bucket->num_items; i++) { + struct tdma_sched_item *item = &bucket->item[i]; + int rc; + + num_events++; + + rc = item->cb(item->p1, item->p2); + if (rc < 0) { + printf("Error %d during processing of item %u of bucket %u\n", + rc, i, sched->cur_bucket); + return rc; + } + /* if the cb() we just called has scheduled more items for the + * current TDMA, bucket->num_items will have increased and we + * will simply continue to execute them as intended */ + } + + /* clear/reset the bucket */ + bucket->num_items = 0; + + /* advance to the next bucket */ + next_bucket = wrap_bucket(1); + sched->cur_bucket = next_bucket; + + /* return number of items that we called */ + return num_events; +} + +void tdma_sched_reset(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + unsigned int bucket_nr; + + for (bucket_nr = 0; bucket_nr < ARRAY_SIZE(sched->bucket); bucket_nr++) { + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + /* current bucket will be reset by iteration code above! */ + if (bucket_nr != sched->cur_bucket) + bucket->num_items = 0; + } + + /* Don't reset cur_bucket, as it would upset the bucket iteration code + * in tdma_sched_execute() */ +} + +void tdma_sched_dump(void) +{ + unsigned int i; + + printf("\n(%2u)", l1s.tdma_sched.cur_bucket); + for (i = 0; i < ARRAY_SIZE(l1s.tdma_sched.bucket); i++) { + int bucket_nr = wrap_bucket(i); + struct tdma_sched_bucket *bucket = &l1s.tdma_sched.bucket[bucket_nr]; + printf("%u:", bucket->num_items); + } + putchar('\n'); +} diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c new file mode 100644 index 000000000..dc9fc34a2 --- /dev/null +++ b/src/target/firmware/layer1/tpu_window.c @@ -0,0 +1,94 @@ +/* TPU window control routines for Layer 1 */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* all units in GSM quarter-bits (923.1ns) */ +#define L1_TDMA_LENGTH_Q 5000 +#define L1_BURST_LENGTH_Q 625 /* L1_TDMA_LENGTH_Q/8 */ + +#define L1_NB_MARGIN_Q (3 * 4) +#define L1_SB_MARGIN_Q (23 * 4) +#define L1_TAIL_DURATION_Q (3 * 4) + +/* Sample length as required by the Calypso DSP */ +#define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */ +#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +#define L1_PW_DURATION_Q 289 + +#define DSP_SETUP_TIME 66 + +static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = { + [L1_RXWIN_PW] = L1_PW_DURATION_Q, + [L1_RXWIN_FB] = L1_FB_DURATION_Q, + [L1_RXWIN_SB] = L1_SB_DURATION_Q, + [L1_RXWIN_NB] = L1_NB_DURATION_Q, +}; + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype) +{ + int16_t start = DSP_SETUP_TIME; + int16_t stop = start + rx_burst_duration[wtype] - 1; + + /* FIXME: AGC */ + /* FIXME: RF PLL */ + + /* window open for TRF6151 */ + /* FIXME: why do we need the magic value 100 ? */ + rffe_mode(gsm_arfcn2band(arfcn), 0); + trf6151_rx_window(start - 100, arfcn, 40, 0); + + /* Window open for ABB */ + twl3025_downlink(1, start); + + /* Delay 11 full TDMA frames */ + if (wtype == L1_RXWIN_FB) { + uint8_t i; + for (i = 0; i < 11; i++) + tpu_enq_at(0); + + stop -= 11 * L1_TDMA_LENGTH_Q; + } + + /* Window close for ABB */ + twl3025_downlink(0, stop); + + /* FIXME: window close for TRF6151 */ +} + +void tpu_end_scenario(void) +{ + tpu_enq_sleep(); + tpu_enable(1); +} diff --git a/src/target/firmware/lib/Makefile b/src/target/firmware/lib/Makefile new file mode 100644 index 000000000..4056e91b8 --- /dev/null +++ b/src/target/firmware/lib/Makefile @@ -0,0 +1,26 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +LIBNAME=mini +CSRCS=vsprintf.c string.c ctype.c printf.c console.c +SSRCS=changebit.S clearbit.S div64.S lib1funcs.S memcpy.S memset.S setbit.S testchangebit.S testclearbit.S testsetbit.S + +COBJS=$(CSRCS:.c=.o) +SOBJS=$(SSRCS:.S=.o) +OBJS=$(COBJS) $(SOBJS) + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +$(COBJS): %.o : %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +$(SOBJS): %.o : %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/lib/bitops.h b/src/target/firmware/lib/bitops.h new file mode 100644 index 000000000..428c9a63d --- /dev/null +++ b/src/target/firmware/lib/bitops.h @@ -0,0 +1,33 @@ + .macro bitop, instr + and r2, r0, #7 + mov r3, #1 + mov r3, r3, lsl r2 + save_and_disable_irqs ip + ldrb r2, [r1, r0, lsr #3] + \instr r2, r2, r3 + strb r2, [r1, r0, lsr #3] + restore_irqs ip + mov pc, lr + .endm + +/** + * testop - implement a test_and_xxx_bit operation. + * @instr: operational instruction + * @store: store instruction + * + * Note: we can trivially conditionalise the store instruction + * to avoid dirting the data cache. + */ + .macro testop, instr, store + add r1, r1, r0, lsr #3 + and r3, r0, #7 + mov r0, #1 + save_and_disable_irqs ip + ldrb r2, [r1] + tst r2, r0, lsl r3 + \instr r2, r2, r0, lsl r3 + \store r2, [r1] + restore_irqs ip + moveq r0, #0 + mov pc, lr + .endm diff --git a/src/target/firmware/lib/changebit.S b/src/target/firmware/lib/changebit.S new file mode 100644 index 000000000..7c709fb38 --- /dev/null +++ b/src/target/firmware/lib/changebit.S @@ -0,0 +1,21 @@ +/* + * linux/arch/arm/lib/changebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* Purpose : Function to change a bit + * Prototype: int change_bit(int bit, void *addr) + */ +ENTRY(_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_change_bit_le) + bitop eor diff --git a/src/target/firmware/lib/clearbit.S b/src/target/firmware/lib/clearbit.S new file mode 100644 index 000000000..cb48f7ac3 --- /dev/null +++ b/src/target/firmware/lib/clearbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/clearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* + * Purpose : Function to clear a bit + * Prototype: int clear_bit(int bit, void *addr) + */ +ENTRY(_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_clear_bit_le) + bitop bic diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c new file mode 100644 index 000000000..f7d0226ec --- /dev/null +++ b/src/target/firmware/lib/console.c @@ -0,0 +1,202 @@ +/* Ringbuffer based serial console layer, imported from OpenPCD */ + +/* (C) 2006-2010 by Harald Welte + * + * 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 +#include +#include +#include + +#include + +struct cons { + char buf[CONS_RB_SIZE]; + char *next_inbyte; + char *next_outbyte; + int initialized; +}; +static struct cons cons; + +void cons_init(void) +{ + memset(cons.buf, 0, sizeof(cons.buf)); + cons.next_inbyte = &cons.buf[0]; + cons.next_outbyte = &cons.buf[0]; + cons.initialized = 1; +} + +/* determine how many bytes are left in the ringbuffer without overwriting + bytes that haven't been written to the console yet */ +static int __cons_rb_space(void) +{ + if (cons.next_inbyte == cons.next_outbyte) + return sizeof(cons.buf)-1; + else if (cons.next_outbyte > cons.next_inbyte) + return (cons.next_outbyte - cons.next_inbyte) -1; + else + return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte); +} + +/* pull one char out of debug ring buffer */ +static int cons_rb_pull(char *ret) +{ + unsigned long flags; + + local_irq_save(flags); + + if (cons.next_outbyte == cons.next_inbyte) { + local_irq_restore(flags); + return -1; + } + + *ret = *cons.next_outbyte; + + cons.next_outbyte++; + if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte = &cons.buf[0]; + } +#if 0 + else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte -= sizeof(cons.buf); + } +#endif + + local_irq_restore(flags); + + return 0; +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static void __rb_flush_wait(void) +{ + char ch; + while (cons_rb_pull(&ch) >= 0) + uart_putchar_wait(CONS_UART_NR, ch); +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static int __rb_flush(void) +{ + while (!uart_tx_busy(CONS_UART_NR)) { + char ch; + if (cons_rb_pull(&ch) < 0) { + /* no more data to write, disable interest in Tx FIFO interrupts */ + return 1; + } + uart_putchar_nb(CONS_UART_NR, ch); + } + + /* if we reach here, UART Tx FIFO is busy again */ + return 0; +} + +/* flush pending data from debug ring buffer to serial port */ +int cons_rb_flush(void) +{ + return __rb_flush(); +} + +static void cons_memcpy(char *pos, const char *data, int len) +{ +#if 0 + /* Somehow our memcpy is broken !?! */ + memcpy(pos, data, len); +#else + int i; + for (i = 0; i < len; i++) + *pos++ = *data++; +#endif +} + +/* Append bytes to ring buffer, not more than we have left! */ +static void __cons_rb_append(const char *data, int len) +{ + if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) { + int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte; + /* copy the first part before we wrap */ + cons_memcpy(cons.next_inbyte, data, before_tail); + data += before_tail; + len -= before_tail; + /* reset the buffer */ + cons.next_inbyte = &cons.buf[0]; + } + cons_memcpy(cons.next_inbyte, data, len); + cons.next_inbyte += len; +} + +/* append bytes to the ringbuffer, do one wrap */ +int cons_rb_append(const char *data, int len) +{ + unsigned long flags; + int bytes_left; + const char *data_cur; + + /* we will never be able to write more than the console buffer */ + if (len > (int) sizeof(cons.buf)) + len = sizeof(cons.buf); + + local_irq_save(flags); + + bytes_left = __cons_rb_space(); + data_cur = data; + + if (len > bytes_left) { + /* append what we can */ + __cons_rb_append(data_cur, bytes_left); + /* busy-wait for all characters to be transmitted */ + __rb_flush_wait(); + /* fill it with the remaining bytes */ + len -= bytes_left; + data_cur += bytes_left; + } + __cons_rb_append(data_cur, len); + + /* we want to get Tx FIFO interrupts */ + uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1); + + local_irq_restore(flags); + + return len; +} + +int cons_puts(const char *s) +{ + if (cons.initialized) { + return cons_rb_append(s, strlen(s)); + } else { + /* if the console is not active yet, we need to fall back */ + int i = strlen(s); + while (i--) + uart_putchar_wait(CONS_UART_NR, *s++); + return i; + } +} + +int cons_putchar(char c) +{ + if (cons.initialized) + return cons_rb_append(&c, 1); + else { + /* if the console is not active yet, we need to fall back */ + uart_putchar_wait(CONS_UART_NR, c); + return 0; + } +} diff --git a/src/target/firmware/lib/copy_template.S b/src/target/firmware/lib/copy_template.S new file mode 100644 index 000000000..cab355c0c --- /dev/null +++ b/src/target/firmware/lib/copy_template.S @@ -0,0 +1,255 @@ +/* + * linux/arch/arm/lib/copy_template.s + * + * Code template for optimized memory copy functions + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This can be used to enable code to cacheline align the source pointer. + * Experiments on tested architectures (StrongARM and XScale) didn't show + * this a worthwhile thing to do. That might be different in the future. + */ +//#define CALGN(code...) code +#define CALGN(code...) + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + * This loads one word from 'ptr', stores it in 'reg' and increments + * 'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + * This loads four or eight words starting from 'ptr', stores them + * in provided registers and increments 'ptr' past those words. + * The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + * It also must apply the condition code if provided, otherwise the + * "al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + * Same as their ldr* counterparts, but data is stored to 'ptr' location + * rather than being loaded. + * + * enter reg1 reg2 + * + * Preserve the provided registers on the stack plus any additional + * data as needed by the implementation including this code. Called + * upon code entry. + * + * exit reg1 reg2 + * + * Restore registers with the values previously saved with the + * 'preserv' macro. Called upon code termination. + */ + + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + subs r2, r2, #32 + str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: nop + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + + add pc, pc, ip + nop + nop + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + + .macro copy_abort_preamble +19: ldmfd sp!, {r5 - r9} + b 21f +20: ldmfd sp!, {r5 - r8} +21: + .endm + + .macro copy_abort_end + ldmfd sp!, {r4, pc} + .endm + diff --git a/src/target/firmware/lib/ctype.c b/src/target/firmware/lib/ctype.c new file mode 100644 index 000000000..6ec51cc25 --- /dev/null +++ b/src/target/firmware/lib/ctype.c @@ -0,0 +1,34 @@ +/* + * linux/lib/ctype.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include + +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + diff --git a/src/target/firmware/lib/div64.S b/src/target/firmware/lib/div64.S new file mode 100644 index 000000000..7eeef50ca --- /dev/null +++ b/src/target/firmware/lib/div64.S @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/lib/div64.S + * + * Optimized computation of 64-bit dividend / 32-bit divisor + * + * Author: Nicolas Pitre + * Created: Oct 5, 2003 + * Copyright: Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +/* + * __do_div64: perform a division with 64-bit dividend and 32-bit divisor. + * + * Note: Calling convention is totally non standard for optimal code. + * This is meant to be used by do_div() from include/asm/div64.h only. + * + * Input parameters: + * xh-xl = dividend (clobbered) + * r4 = divisor (preserved) + * + * Output values: + * yh-yl = result + * xh = remainder + * + * Clobbered regs: xl, ip + */ + +ENTRY(__do_div64) + + @ Test for easy paths first. + subs ip, r4, #1 + bls 9f @ divisor is 0 or 1 + tst ip, r4 + beq 8f @ divisor is power of 2 + + @ See if we need to handle upper 32-bit result. + cmp xh, r4 + mov yh, #0 + blo 3f + + @ Align divisor with upper part of dividend. + @ The aligned divisor is stored in yl preserving the original. + @ The bit position is stored in ip. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz yl, r4 + clz ip, xh + sub yl, yl, ip + mov ip, #1 + mov ip, ip, lsl yl + mov yl, r4, lsl yl + +#else + + mov yl, r4 + mov ip, #1 +1: cmp yl, #0x80000000 + cmpcc yl, xh + movcc yl, yl, lsl #1 + movcc ip, ip, lsl #1 + bcc 1b + +#endif + + @ The division loop for needed upper bit positions. + @ Break out early if dividend reaches 0. +2: cmp xh, yl + orrcs yh, yh, ip + subcss xh, xh, yl + movnes ip, ip, lsr #1 + mov yl, yl, lsr #1 + bne 2b + + @ See if we need to handle lower 32-bit result. +3: cmp xh, #0 + mov yl, #0 + cmpeq xl, r4 + movlo xh, xl + movlo pc, lr + + @ The division loop for lower bit positions. + @ Here we shift remainer bits leftwards rather than moving the + @ divisor for comparisons, considering the carry-out bit as well. + mov ip, #0x80000000 +4: movs xl, xl, lsl #1 + adcs xh, xh, xh + beq 6f + cmpcc xh, r4 +5: orrcs yl, yl, ip + subcs xh, xh, r4 + movs ip, ip, lsr #1 + bne 4b + mov pc, lr + + @ The top part of remainder became zero. If carry is set + @ (the 33th bit) this is a false positive so resume the loop. + @ Otherwise, if lower part is also null then we are done. +6: bcs 5b + cmp xl, #0 + moveq pc, lr + + @ We still have remainer bits in the low part. Bring them up. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz xh, xl @ we know xh is zero here so... + add xh, xh, #1 + mov xl, xl, lsl xh + mov ip, ip, lsr xh + +#else + +7: movs xl, xl, lsl #1 + mov ip, ip, lsr #1 + bcc 7b + +#endif + + @ Current remainder is now 1. It is worthless to compare with + @ divisor at this point since divisor can not be smaller than 3 here. + @ If possible, branch for another shift in the division loop. + @ If no bit position left then we are done. + movs ip, ip, lsr #1 + mov xh, #1 + bne 4b + mov pc, lr + +8: @ Division by a power of 2: determine what that divisor order is + @ then simply shift values around + +#if __LINUX_ARM_ARCH__ >= 5 + + clz ip, r4 + rsb ip, ip, #31 + +#else + + mov yl, r4 + cmp r4, #(1 << 16) + mov ip, #0 + movhs yl, yl, lsr #16 + movhs ip, #16 + + cmp yl, #(1 << 8) + movhs yl, yl, lsr #8 + addhs ip, ip, #8 + + cmp yl, #(1 << 4) + movhs yl, yl, lsr #4 + addhs ip, ip, #4 + + cmp yl, #(1 << 2) + addhi ip, ip, #3 + addls ip, ip, yl, lsr #1 + +#endif + + mov yh, xh, lsr ip + mov yl, xl, lsr ip + rsb ip, ip, #32 + orr yl, yl, xh, lsl ip + mov xh, xl, lsl ip + mov xh, xh, lsr ip + mov pc, lr + + @ eq -> division by 1: obvious enough... +9: moveq yl, xl + moveq yh, xh + moveq xh, #0 + moveq pc, lr + + @ Division by 0: + str lr, [sp, #-8]! + bl __div0 + + @ as wrong as it could be... + mov yl, #0 + mov yh, #0 + mov xh, #0 + ldr pc, [sp], #8 + diff --git a/src/target/firmware/lib/lib1funcs.S b/src/target/firmware/lib/lib1funcs.S new file mode 100644 index 000000000..b02a85ebc --- /dev/null +++ b/src/target/firmware/lib/lib1funcs.S @@ -0,0 +1,334 @@ +/* + * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines + * + * Author: Nicolas Pitre + * - contributed to gcc-3.4 on Sep 30, 2003 + * - adapted for the Linux kernel on Oct 2, 2003 + */ + +/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc. + +This file 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, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include +#include + + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + +.macro ARM_MOD_BODY dividend, divisor, order, spare + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + clz \spare, \dividend + sub \order, \order, \spare + mov \divisor, \divisor, lsl \order + +#else + + mov \order, #0 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + addlo \order, \order, #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + addlo \order, \order, #1 + blo 1b + +#endif + + @ Perform all needed substractions to keep only the reminder. + @ Do comparisons in batch of 4 first. + subs \order, \order, #3 @ yes, 3 is intended here + blt 2f + +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + cmp \dividend, #1 + mov \divisor, \divisor, lsr #4 + subges \order, \order, #4 + bge 1b + + tst \order, #3 + teqne \dividend, #0 + beq 5f + + @ Either 1, 2 or 3 comparison/substractions are left. +2: cmn \order, #2 + blt 4f + beq 3f + cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +3: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +4: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor +5: +.endm + + +ENTRY(__udivsi3) +ENTRY(__aeabi_uidiv) + + subs r2, r1, #1 + moveq pc, lr + bcc Ldiv0 + cmp r0, r1 + bls 11f + tst r1, r2 + beq 12f + + ARM_DIV_BODY r0, r1, r2, r3 + + mov r0, r2 + mov pc, lr + +11: moveq r0, #1 + movne r0, #0 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + mov r0, r0, lsr r2 + mov pc, lr + + +ENTRY(__umodsi3) + + subs r2, r1, #1 @ compare divisor with 1 + bcc Ldiv0 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + movls pc, lr + + ARM_MOD_BODY r0, r1, r2, r3 + + mov pc, lr + + +ENTRY(__divsi3) +ENTRY(__aeabi_idiv) + + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + + +ENTRY(__modsi3) + + cmp r1, #0 + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + movs ip, r0 @ preserve sign of dividend + rsbmi r0, r0, #0 @ if negative make positive + subs r2, r1, #1 @ compare divisor with 1 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + bls 10f + + ARM_MOD_BODY r0, r1, r2, r3 + +10: cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +ENTRY(__aeabi_uidivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +ENTRY(__aeabi_idivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-8]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #8 + +ENTRY(__div0) + mov pc, lr diff --git a/src/target/firmware/lib/memcpy.S b/src/target/firmware/lib/memcpy.S new file mode 100644 index 000000000..2bbd5692e --- /dev/null +++ b/src/target/firmware/lib/memcpy.S @@ -0,0 +1,59 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .macro ldr1w ptr reg abort + ldr \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + str \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +ENTRY(memcpy) + +#include "copy_template.S" + diff --git a/src/target/firmware/lib/memset.S b/src/target/firmware/lib/memset.S new file mode 100644 index 000000000..04e254a80 --- /dev/null +++ b/src/target/firmware/lib/memset.S @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/lib/memset.S + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include +#include + + .text + .align 5 + .word 0 + +1: subs r2, r2, #4 @ 1 do we have enough + blt 5f @ 1 bytes to align with? + cmp r3, #2 @ 1 + strltb r1, [r0], #1 @ 1 + strleb r1, [r0], #1 @ 1 + strb r1, [r0], #1 @ 1 + add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) +/* + * The pointer is now aligned and the length is adjusted. Try doing the + * memzero again. + */ + +ENTRY(memset) + ands r3, r0, #3 @ 1 unaligned? + bne 1b @ 1 +/* + * we know that the pointer in r0 is aligned to a word boundary. + */ + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + mov r3, r1 + cmp r2, #16 + blt 4f +/* + * We need an extra register for this loop - save the return address and + * use the LR + */ + str lr, [sp, #-4]! + mov ip, r1 + mov lr, r1 + +2: subs r2, r2, #64 + stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + bgt 2b + LOADREGS(eqfd, sp!, {pc}) @ Now <64 bytes to go. +/* + * No need to correct the count; we're only testing bits from now on + */ + tst r2, #32 + stmneia r0!, {r1, r3, ip, lr} + stmneia r0!, {r1, r3, ip, lr} + tst r2, #16 + stmneia r0!, {r1, r3, ip, lr} + ldr lr, [sp], #4 + +4: tst r2, #8 + stmneia r0!, {r1, r3} + tst r2, #4 + strne r1, [r0], #4 +/* + * When we get here, we've got less than 4 bytes to zero. We + * may have an unaligned pointer as well. + */ +5: tst r2, #2 + strneb r1, [r0], #1 + strneb r1, [r0], #1 + tst r2, #1 + strneb r1, [r0], #1 + RETINSTR(mov,pc,lr) diff --git a/src/target/firmware/lib/printf.c b/src/target/firmware/lib/printf.c new file mode 100644 index 000000000..a4fc68761 --- /dev/null +++ b/src/target/firmware/lib/printf.c @@ -0,0 +1,19 @@ + +#include +#include + +static char printf_buffer[1024]; + +int printf(const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = vsnprintf(printf_buffer, sizeof(printf_buffer), fmt, args); + va_end(args); + + puts(printf_buffer); + + return r; +} diff --git a/src/target/firmware/lib/setbit.S b/src/target/firmware/lib/setbit.S new file mode 100644 index 000000000..9009bc1eb --- /dev/null +++ b/src/target/firmware/lib/setbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/setbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* + * Purpose : Function to set a bit + * Prototype: int set_bit(int bit, void *addr) + */ +ENTRY(_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_set_bit_le) + bitop orr diff --git a/src/target/firmware/lib/string.c b/src/target/firmware/lib/string.c new file mode 100644 index 000000000..437373bdf --- /dev/null +++ b/src/target/firmware/lib/string.c @@ -0,0 +1,50 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + * + * * Sat Feb 09 2002, Jason Thomas , + * Matthew Hawkins + * - Kissed strtok() goodbye + */ + +#include +#include +#include + + +#ifndef __HAVE_ARCH_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/src/target/firmware/lib/testchangebit.S b/src/target/firmware/lib/testchangebit.S new file mode 100644 index 000000000..37c303e3d --- /dev/null +++ b/src/target/firmware/lib/testchangebit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testchangebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_change_bit_le) + testop eor, strb diff --git a/src/target/firmware/lib/testclearbit.S b/src/target/firmware/lib/testclearbit.S new file mode 100644 index 000000000..985c39960 --- /dev/null +++ b/src/target/firmware/lib/testclearbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testclearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_clear_bit_le) + testop bicne, strneb diff --git a/src/target/firmware/lib/testsetbit.S b/src/target/firmware/lib/testsetbit.S new file mode 100644 index 000000000..4a8a164b9 --- /dev/null +++ b/src/target/firmware/lib/testsetbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testsetbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_set_bit_le) + testop orreq, streqb diff --git a/src/target/firmware/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c new file mode 100644 index 000000000..81057e4de --- /dev/null +++ b/src/target/firmware/lib/vsprintf.c @@ -0,0 +1,847 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +/* + * Fri Jul 13 2001 Crutcher Dunnavant + * - changed to provide snprintf and vsnprintf functions + * So Feb 1 16:51:32 CET 2004 Juergen Quade + * - scnprintf and vscnprintf + */ + +#include +#include +#include +#include +#include +#include + +#include + +/** + * simple_strtoul - convert a string to an unsigned long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * simple_strtol - convert a string to a signed long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * simple_strtoll - convert a string to a signed long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long long simple_strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoull(cp+1,endp,base); + return simple_strtoull(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if ((signed long long) num < 0) { + sign = '-'; + num = - (signed long long) num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +/** + * vsnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which would + * be generated for the given input, excluding the trailing + * '\0', as per ISO C99. If you want to have the exact + * number of characters written into @buf as return value + * (not including the trailing '\0'), use vscnprintf. If the + * return is greater than or equal to @size, the resulting + * string is truncated. + * + * Call this function if you are already dealing with a va_list. + * You probably want snprintf instead. + */ +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* Reject out-of-range values early */ + if ((int) size < 0) { + return 0; + } + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + continue; + + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z' || qualifier == 'z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z' || qualifier == 'z') { + num = va_arg(args, size_t); + } else if (qualifier == 't') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + + +/** + * vscnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which have been written into + * the @buf not including the trailing '\0'. If @size is <= 0 the function + * returns 0. + * + * Call this function if you are already dealing with a va_list. + * You probably want scnprintf instead. + */ +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + unsigned int i; + + i=vsnprintf(buf,size,fmt,args); + return (i >= size) ? (size - 1) : i; +} + + +/** + * snprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. If the return is greater than or equal to + * @size, the resulting string is truncated. + */ +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + + +/** + * scnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters written into @buf not including + * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is + * greater than or equal to @size, the resulting string is truncated. + */ + +int scnprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + unsigned int i; + + va_start(args, fmt); + i = vsnprintf(buf, size, fmt, args); + va_end(args); + return (i >= size) ? (size - 1) : i; +} + +/** + * vsprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use vsnprintf or vscnprintf in order to avoid + * buffer overflows. + * + * Call this function if you are already dealing with a va_list. + * You probably want sprintf instead. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, INT_MAX, fmt, args); +} + + +/** + * sprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use snprintf or scnprintf in order to avoid + * buffer overflows. + */ +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + return i; +} + + +/** + * vsscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: format of buffer + * @args: arguments + */ +int vsscanf(const char * buf, const char * fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int qualifier; + int base; + int field_width; + int is_sign = 0; + + while(*fmt && *str) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if (isspace(*fmt)) { + while (isspace(*fmt)) + ++fmt; + while (isspace(*str)) + ++str; + } + + /* anything that is not a conversion must match exactly */ + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + + if (!*fmt) + break; + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + + /* get conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z') { + qualifier = *fmt++; + if (qualifier == *fmt) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + base = 10; + is_sign = 0; + + if (!*fmt || !*str) + break; + + switch(*fmt++) { + case 'c': + { + char *s = (char *) va_arg(args,char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *) va_arg(args, char *); + if(field_width == -1) + field_width = INT_MAX; + /* first, skip leading white space in buffer */ + while (isspace(*str)) + str++; + + /* now copy until next white space */ + while (*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + *s = '\0'; + num++; + } + continue; + case 'n': + /* return number of characters read so far */ + { + int *i = (int *)va_arg(args,int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + /* looking for '%' in str */ + if (*str++ != '%') + return num; + continue; + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + while (isspace(*str)) + str++; + + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if (is_sign) { + signed char *s = (signed char *) va_arg(args,signed char *); + *s = (signed char) simple_strtol(str,&next,base); + } else { + unsigned char *s = (unsigned char *) va_arg(args, unsigned char *); + *s = (unsigned char) simple_strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *) va_arg(args,short *); + *s = (short) simple_strtol(str,&next,base); + } else { + unsigned short *s = (unsigned short *) va_arg(args, unsigned short *); + *s = (unsigned short) simple_strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *) va_arg(args,long *); + *l = simple_strtol(str,&next,base); + } else { + unsigned long *l = (unsigned long*) va_arg(args,unsigned long*); + *l = simple_strtoul(str,&next,base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long*) va_arg(args,long long *); + *l = simple_strtoll(str,&next,base); + } else { + unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*); + *l = simple_strtoull(str,&next,base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t*) va_arg(args,size_t*); + *s = (size_t) simple_strtoul(str,&next,base); + } + break; + default: + if (is_sign) { + int *i = (int *) va_arg(args, int*); + *i = (int) simple_strtol(str,&next,base); + } else { + unsigned int *i = (unsigned int*) va_arg(args, unsigned int*); + *i = (unsigned int) simple_strtoul(str,&next,base); + } + break; + } + num++; + + if (!next) + break; + str = next; + } + return num; +} + + +/** + * sscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: formatting of buffer + * @...: resulting arguments + */ +int sscanf(const char * buf, const char * fmt, ...) +{ + va_list args; + int i; + + va_start(args,fmt); + i = vsscanf(buf,fmt,args); + va_end(args); + return i; +} + +/* generic puts() implementation independent of who provides putchar() */ +int puts(const char *s) +{ +#ifdef ARCH_HAS_CONSOLE + return _puts(s); +#else + while (1) { + char c = *s++; + if (c == 0) + return; + putchar(c); + } + return 0; +#endif +} diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c new file mode 100644 index 000000000..ea009858a --- /dev/null +++ b/src/target/firmware/rf/trf6151.c @@ -0,0 +1,380 @@ +/* Driver for RF Transceiver Circuit (TRF6151) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + +#include + +enum trf6151_reg { + REG_RX = 0, /* RF general settings */ + REG_PLL = 1, /* PLL settings */ + REG_PWR = 2, /* Power on/off funcitonal blocks */ + REG_CFG = 3, /* Transceiver and PA controller settings */ + REG_TEST1 = 4, + REG_TEST2 = 5, + REG_TEST3 = 6, + REG_TEST4 = 7, + _MAX_REG +}; + +/* REG_RX */ +#define RX_READ_EN (1 << 7) +#define RX_CAL_MODE (1 << 8) +#define RX_RF_GAIN_HIGH (3 << 9) +#define RX_VGA_GAIN_SHIFT 11 + +/* REG_PWR */ +#define PWR_BANDGAP_SHIFT 3 +#define PWR_BANDGAP_OFF (0 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON_SPEEDUP (2 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON (3 << PWR_BANDGAP_SHIFT) +#define PWR_REGUL_ON (1 << 5) +#define PWR_SYNTHE_OFF (0) +#define PWR_SYNTHE_RX_ON (1 << 9) +#define PWR_SYNTHE_TX_ON (1 << 10) +#define PWR_RX_MODE (1 << 11) +#define PWR_TX_MODE (1 << 13) +#define PWR_PACTRL_APC (1 << 14) +#define PWR_PACTRL_APCEN (1 << 15) + +/* REG_CFG */ +#define CFG_TX_LOOP_MANU (1 << 3) +#define CFG_PACTLR_IDIOD_30uA (0 << 4) +#define CFG_PACTLR_IDIOD_300uA (1 << 4) +#define CFG_PACTLR_RES_OPEN (0 << 10) +#define CFG_PACTLR_RES_150k (1 << 10) +#define CFG_PACTLR_RES_300k (2 << 10) +#define CFG_PACTLR_CAP_0pF (0 << 12) +#define CFG_PACTLR_CAP_12p5F (1 << 12) +#define CFG_PACTLR_CAP_25pF (3 << 12) +#define CFG_PACTLR_CAP_50pF (2 << 12) +#define CFG_TEMP_SENSOR (1 << 14) +#define CFG_ILOGIC_INIT_DIS (1 << 15) + +/* FIXME: This must be defined in the RFFE configuration */ +#define TRF6151_TSP_UID 2 +#define TRF6151_PACTRL_CFG (CFG_PACTLR_RES_OPEN|CFG_PACTLR_CAP_0pF|CFG_PACTLR_IDIOD_30uA) + +#define PLL_VAL(a, b) ((a << 3) | (((b)-64) << 9)) + +/* All values in qbits unless otherwise speciifed */ +#define TRF6151_LDO_DELAY_TS 6 /* six TDMA frames (at least 25ms) */ +#define TRF6151_RX_PLL_DELAY 184 /* 170 us */ +#define TRF6151_TX_PLL_DELAY 260 /* 170 us */ + +uint16_t rf_arfcn = 871; /* TODO: this needs to be private */ +static uint16_t rf_band; + +static uint16_t trf6151_reg_cache[_MAX_REG] = { + [REG_RX] = 0x9E00, + [REG_PLL] = 0x0000, + [REG_PWR] = 0x0000, + [REG_CFG] = 0x2980, +}; + +/* Write to a TRF6151 register (4 TPU instructions) */ +static void trf6151_reg_write(uint16_t reg, uint16_t val) +{ + printd("trf6151_reg_write(reg=%u, val=0x%04x)\n", reg, val); + /* each TSP write takes 4 TPU instructions */ + tsp_write(TRF6151_TSP_UID, 16, (reg | val)); + trf6151_reg_cache[reg] = val; +} + +int trf6151_set_gain(uint8_t dbm, int high) +{ + uint16_t reg = trf6151_reg_cache[REG_RX] & 0x07ff; + + if (dbm < 14 || dbm > 40) + return -1; + + /* clear the gain bits first */ + reg &= ~((0x1F) << RX_VGA_GAIN_SHIFT); + /* OR-in the new gain value */ + reg |= (6 + (dbm-14)/2) << RX_VGA_GAIN_SHIFT; + + if (high) + reg |= RX_RF_GAIN_HIGH; + else + reg &= ~RX_RF_GAIN_HIGH; + + trf6151_reg_write(REG_RX, reg); + + return 0; +} + +#define SCALE_100KHZ 100 + +/* Compute TRF6151 PLL valuese for all 4 RX bands */ +static uint16_t trf6151_pll_rx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l; + uint32_t b10; /* B value expanded by a factor of 10 */ + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) + l = 4; + else + l = 2; + + /* To compute B, we assume A is zero */ + b = (freq_100khz * 65 * l) / (64 * 26 * 10); + + if ((l == 4 && (b < 135 || b > 150)) || + (l == 2 && (b < 141 || b > 155))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10) / (65 * l); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + if (l == 2) + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +enum trf6151_pwr_unit { + TRF1651_PACTLR_APC, + TRF6151_PACTRL_APCEN, + TRF6151_TRANSMITTER, + TRF6151_REGULATORS, +}; + +enum trf6151_gsm_band { + GSM900 = 1, + GSM1800 = 2, + GSM850_LOW = 4, + GSM850_HIGH = 5, + GSM1900 = 6, +}; + +static inline void trf6151_reset(void) +{ + /* pull the nRESET line low */ + tsp_act_disable((1 << 0)); + tpu_enq_wait(50); + /* release nRESET */ + tsp_act_enable((1 << 0)); +} + +void trf6151_init(void) +{ + /* Configure TSPEN0, which is connected to TWL3025, + * FIXME: why is this here and not in the TWL3025 driver? */ + tsp_setup(0, 1, 0, 0); + /* Configure TSPEN2, which is connected ot TRF6151 STROBE */ + tsp_setup(TRF6151_TSP_UID, 0, 1, 1); + + trf6151_reset(); + + /* configure TRF6151 for operation */ + trf6151_power(1); + trf6151_reg_write(REG_CFG, TRF6151_PACTRL_CFG | CFG_ILOGIC_INIT_DIS); + + /* FIXME: Uplink / Downlink Calibration */ +} + +void trf6151_power(int on) +{ + if (on) { + trf6151_reg_write(REG_PWR, PWR_REGUL_ON | PWR_BANDGAP_ON); + /* wait until regulators are stable (25ms == 27100 qbits) */ + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(2100); + } else + trf6151_reg_write(REG_PWR, PWR_BANDGAP_ON); +} + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode) +{ + uint16_t pwr = (PWR_REGUL_ON | PWR_BANDGAP_ON | (rf_band<<6)); + + switch (mode) { + case TRF6151_IDLE: + /* should we switch of the RF gain for power saving? */ + break; + case TRF6151_RX: + pwr |= (PWR_SYNTHE_RX_ON | PWR_RX_MODE); + break; + case TRF6151_TX: + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE); + break; + } + trf6151_reg_write(REG_PWR, pwr); +} + +static void trf6151_band_select(enum trf6151_gsm_band band) +{ + uint16_t pwr = trf6151_reg_cache[REG_PWR]; + + pwr &= ~(3 << 6); + pwr |= (band << 6); + + trf6151_reg_write(REG_PWR, pwr); +} + +/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink) +{ + uint32_t freq_khz; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_850: + rf_band = GSM850_LOW; /* FIXME: what about HIGH */ + break; + case GSM_900: + rf_band = GSM900; + break; + case GSM_1800: + rf_band = GSM1800; + break; + case GSM_1900: + rf_band = GSM1900; + break; + case GSM_450: + case GSM_480: + case GSM_750: + case GSM_810: + printf("Unsupported rf_band.\n"); + break; + } + + trf6151_band_select(rf_band); + + freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100; + printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz); + + if (uplink == 0) + trf6151_reg_write(REG_PLL, trf6151_pll_rx(freq_khz)); + else + printf("We don't support uplink tuning yet!\n"); + + rf_arfcn = arfcn; // TODO: arfcn is referenced at other places +} + +void trf6151_calib_dc_offs(void) +{ + uint16_t rx = trf6151_reg_cache[REG_RX]; + + /* Set RX CAL Mode bit, it will re-set automatically */ + trf6151_reg_write(REG_RX, rx | RX_CAL_MODE); + /* DC offset calibration can take up to 50us, i.e. 54.16 * 923ns*/ + tpu_enq_wait(55); +} + +/* Frontend gain can be switched high or low (dB) */ +#define TRF6151_FE_GAIN_LOW 7 +#define TRF6151_FE_GAIN_HIGH 27 + +/* VGA at baseband can be adjusted in this range (dB) */ +#define TRF6151_VGA_GAIN_MIN 14 +#define TRF6151_VGA_GAIN_MAX 40 + +uint8_t trf6151_get_gain(void) +{ + uint16_t vga, reg_rx = trf6151_reg_cache[REG_RX]; + uint8_t gain = 0; + + switch ((reg_rx >> 9) & 3) { + case 0: + gain += TRF6151_FE_GAIN_LOW; + break; + case 3: + gain += TRF6151_FE_GAIN_HIGH; + break; + } + + vga = (reg_rx >> RX_VGA_GAIN_SHIFT) & 0x1f; + if (vga < 6) + vga = 6; + + gain += TRF6151_VGA_GAIN_MIN + (vga - 6) * 2; + + return gain; +} + +void trf6151_test(uint16_t arfcn) +{ + /* Select ARFCN 871 downlink */ + trf6151_set_arfcn(arfcn, 0); + trf6151_set_gain(40, 0); + + trf6151_set_mode(TRF6151_RX); + //trf6151_reg_write(REG_PWR, (PWR_SYNTHE_RX_ON | PWR_RX_MODE | PWR_REGUL_ON | (rf_band<<6) | PWR_BANDGAP_ON)); + /* Wait for PLL stabilization (170us max) */ + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + /* Use DC offset calibration after RX mode has been switched on + * (might not be needed) */ + trf6151_calib_dc_offs(); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +#define TRF6151_REGWR_QBITS 8 /* 4 GSM qbits + 4 TPU instructions */ +#define TRF6151_RX_TPU_INSTR 4 /* set_gain(1), set_arfcn(2), set_mode(1) */ + +/* delay caused by this driver programming the TPU for RX mode */ +#define TRF6151_RX_TPU_DELAY (TRF6151_RX_TPU_INSTR * TRF6151_REGWR_QBITS) + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn, uint8_t vga_dbm, int rf_gain_high) +{ + int16_t start_pll_qbits; + + /* Set the AGC and PLL registers right now, not time critical */ + trf6151_set_gain(vga_dbm, rf_gain_high); + trf6151_set_arfcn(arfcn, 0); + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_RX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + trf6151_set_mode(TRF6151_RX); + + /* FIXME: power down at the right time again */ +} diff --git a/src/target_dsp/.gitignore b/src/target_dsp/.gitignore new file mode 100644 index 000000000..5cf144ea2 --- /dev/null +++ b/src/target_dsp/.gitignore @@ -0,0 +1,4 @@ +*.o +*.a +*.coff +*.bin diff --git a/src/target_dsp/calypso/Makefile b/src/target_dsp/calypso/Makefile new file mode 100644 index 000000000..ff21e694a --- /dev/null +++ b/src/target_dsp/calypso/Makefile @@ -0,0 +1,7 @@ +dsp_dump.bin: bl_stage3.S dsp_dump.lds + c54x-coff-as bl_stage3.S -o bl_stage3.o + c54x-coff-ld --script dsp_dump.lds bl_stage3.o -o dsp_dump.coff + c54x-coff-objcopy -j .text -O binary dsp_dump.coff dsp_dump.bin + +clean: + rm -f *.o *.bin *.coff diff --git a/src/target_dsp/calypso/bin2cfile.py b/src/target_dsp/calypso/bin2cfile.py new file mode 100755 index 000000000..51401b8f7 --- /dev/null +++ b/src/target_dsp/calypso/bin2cfile.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +import struct +import sys + +def group_by_n(s, n, do_join=True): + return ( ''.join(x) for x in zip(*[s[i::n] for i in range(n)]) ) + + +def main(pn, filename): + # Get all bytes + f = open(filename, 'r') + d = f.read() + f.close() + + # Get the data + ops = ''.join([ + '0x%04x,%c' % ( + struct.unpack('=H', x)[0], + '\n' if (i&3==3) else ' ' + ) + for i, x + in enumerate(group_by_n(d, 2)) + ])[:-1] + + # Header / footer + print """ +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_xxx[] = { + { + .addr = 0x, + .size = 0x%04x, + .data = _SA_DECL { +%s + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#undef _SA_DECL +""" % (len(d)/2, ops) + + +if __name__ == "__main__": + main(*sys.argv) diff --git a/src/target_dsp/calypso/bl_stage3.S b/src/target_dsp/calypso/bl_stage3.S new file mode 100644 index 000000000..402c3c597 --- /dev/null +++ b/src/target_dsp/calypso/bl_stage3.S @@ -0,0 +1,142 @@ + +BCSR .equ 0x29 + +CMD_IDLE .equ 1 ; Do nothing / DSP ready for commands +CMD_COPY_BLOCK .equ 2 ; (if size == 0, then exec) +CMD_COPY_MODE .equ 4 ; Select copy mode + ; (0=code write, 1=data write, + ; 2=code read, 3=data read, + ; 4=prom read, 5=drom read) +CMD_VERSION .equ 0xffff ; API_RAM[0] = bootloader version + +VERSION .equ 0x0100 ; 1.00 + + + .section .apiram + + .org 0x07fc +bl_addr_hi .ds 1 +bl_size .ds 1 +bl_addr_lo .ds 1 +bl_status .ds 1 + + + .text + .mmregs +_start: + orm #2, *(BCSR) ; ? + + ld #0x1f, DP + stm #0x1100, SP + stm #0, AR4 +_done: + stm #_api_ram, AR2 + st #CMD_IDLE, @bl_status +_loop: + ; Version + cmpm @bl_status, #CMD_VERSION + bc 1f, ntc + + bd _done + st #VERSION, *AR2 +1: + + ; Select copy mode + cmpm @bl_status, #CMD_COPY_MODE + bc 1f, ntc + + bd _done + mvdm @_api_ram, AR4 +1: + + ; Copy + cmpm @bl_status, #CMD_COPY_BLOCK + bc _loop, ntc + + ; Capture values for copy operations + ; A = full address + ; AR1 size-1 + ; AR2 api_ram (set previously) + ; AR3 data/code address + ; AR4 mode + + ldu @bl_addr_lo, A + stlm A, AR3 + add @bl_addr_hi, 16, A + + ldu @bl_size, B + stlm B, AR1 + ; mar *AR1- ; We do this in a delay slot later on ... + + ; Start + bc 1f, bneq ; B still contains size + bacc A + +1: + ; Select + stm #AR4, AR5 ; AR5 = &AR4 + bit *AR5, 13 ; Test mode(2) + bcd _read_rom, tc + mar *AR1- + bit *AR5, 15 ; Test mode(0) lsb + bcd _copy_data, tc + bit *AR5, 14 ; Test mode(1) + nop + + ; Copy to/from Program space +_copy_prog: + bc _read_prog, tc + + ; Copy from API -> prog space (mode 0) +_write_prog: + rpt *(AR1) + writa *AR2+ + b _done + + ; Copy from prog space -> API (mode 2) +_read_prog: + rpt *(AR1) + reada *AR2+ + b _done + + ; Copy to/from Data space +_copy_data: + bc _read_data, tc + + ; Copy from API -> data space (mode 1) +_write_data: + rpt *(AR1) + mvdd *AR2+, *AR3+ + b _done + + ; Copy from data space -> API (mode 3) +_read_data: + rpt *(AR1) + mvdd *AR3+, *AR2+ + b _done + + ; Read from {D,P}ROM bypassing protection +_read_rom: + ldm AR1, B ; Can't put those two ops in the delay slot of + stlm B, BRC ; 'bc' because of unprotected pipeline conflicts + bc _read_rom_data, tc + +_read_rom_prog: + rptb 1f - 1 + call prom_read_xplt +1: + b _done + +_read_rom_data: + rptb 1f - 1 + call drom_read_xplt +1: + b _done + + +drom_read_xplt .equ 0xe4b8 +prom_read_xplt .equ 0x7213 + + + .end + diff --git a/src/target_dsp/calypso/dsp_dump.lds b/src/target_dsp/calypso/dsp_dump.lds new file mode 100644 index 000000000..566330261 --- /dev/null +++ b/src/target_dsp/calypso/dsp_dump.lds @@ -0,0 +1,22 @@ +OUTPUT_FORMAT("coff1-c54x") +OUTPUT_ARCH("") +MEMORY +{ + apiram (RWXI) : ORIGIN = 0x0800, LENGTH = 0x2000 +} +SECTIONS +{ + . = 0x0800; + + .apiram : + { + PROVIDE(_api_ram = .); + *(.apiram) + } > apiram + + .text : + { + *(.text) + } > apiram +} +