From 1393fa3ea3ba652c2ad3e3ecd206e9ec086a85e7 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Wed, 16 Jan 2013 23:05:44 +0100 Subject: fw/l1: Initial import of new BTS primitive Signed-off-by: Sylvain Munaut --- src/target/firmware/include/layer1/mframe_sched.h | 2 + src/target/firmware/include/layer1/prim.h | 2 + src/target/firmware/include/layer1/sync.h | 6 + src/target/firmware/layer1/mframe_sched.c | 8 + src/target/firmware/layer1/prim_bts.c | 338 ++++++++++++++++++++++ 5 files changed, 356 insertions(+) create mode 100644 src/target/firmware/layer1/prim_bts.c diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h index 74e2d271..7494eebc 100644 --- a/src/target/firmware/include/layer1/mframe_sched.h +++ b/src/target/firmware/include/layer1/mframe_sched.h @@ -36,6 +36,8 @@ enum mframe_task { MF_TASK_NEIGH_PM26E, MF_TASK_NEIGH_PM26O, + MF_TASK_BTS, + /* Test task: send Normal Burst in all timeslots */ MF_TASK_UL_ALL_NB, }; diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h index 30c51ae6..654daa2f 100644 --- a/src/target/firmware/include/layer1/prim.h +++ b/src/target/firmware/include/layer1/prim.h @@ -31,4 +31,6 @@ extern const struct tdma_sched_item tch_a_sched_set[]; extern const struct tdma_sched_item tch_d_sched_set[]; extern const struct tdma_sched_item neigh_pm_sched_set[]; +extern const struct tdma_sched_item bts_sched_set[]; + #endif /* _L1_PRIM_H */ diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index dae85a1a..2cb5152c 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -154,6 +154,12 @@ struct l1s_state { uint8_t tn[64]; uint8_t level[64]; } neigh_pm; + + /* bts mode */ + struct { + uint16_t arfcn; + uint8_t bsic; + } bts; }; extern struct l1s_state l1s; diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 0a9ff1e4..c55b809b 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -310,6 +310,12 @@ static const struct mframe_sched_item mf_neigh_pm26_odd[] = { { .sched_set = NULL } }; +/* BTS */ +static const struct mframe_sched_item mf_bts[] = { + { .sched_set = bts_sched_set, .modulo = 1, .frame_nr = 0 }, + { .sched_set = NULL } +}; + /* Test TX */ static const struct mframe_sched_item mf_tx_all_nb[] = { { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 }, @@ -349,6 +355,8 @@ static const struct mframe_sched_item *sched_set_for_task[32] = { [MF_TASK_NEIGH_PM26E] = mf_neigh_pm26_even, [MF_TASK_NEIGH_PM26O] = mf_neigh_pm26_odd, + [MF_TASK_BTS] = mf_bts, + [MF_TASK_UL_ALL_NB] = mf_tx_all_nb, }; diff --git a/src/target/firmware/layer1/prim_bts.c b/src/target/firmware/layer1/prim_bts.c new file mode 100644 index 00000000..627cf36b --- /dev/null +++ b/src/target/firmware/layer1/prim_bts.c @@ -0,0 +1,338 @@ +/* Layer 1 - BTS functions */ + +/* (C) 2012 by 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 + + +/* ------------------------------------------------------------------------ */ +/* DSP extensions API */ +/* ------------------------------------------------------------------------ */ + +#define BASE_API 0xFFD00000 + +#define API_DSP2ARM(x) (BASE_API + ((x) - 0x0800) * sizeof(uint16_t)) + +#define BASE_API_EXT_DB0 API_DSP2ARM(0x2000) +#define BASE_API_EXT_DB1 API_DSP2ARM(0x2200) +#define BASE_API_EXT_NDB API_DSP2ARM(0x2400) + +struct dsp_ext_ndb { + uint16_t active; + uint16_t tsc; +} __attribute__((packed)); + +struct dsp_ext_db { + /* TX command and data ptr */ + struct { +#define DSP_EXT_TX_CMD_NONE 0 +#define DSP_EXT_TX_CMD_FB 1 +#define DSP_EXT_TX_CMD_SB 2 +#define DSP_EXT_TX_CMD_DUMMY 3 +#define DSP_EXT_TX_CMD_AB 4 +#define DSP_EXT_TX_CMD_NB 5 + uint16_t cmd; + uint16_t data; + } tx[8]; + + /* RX command and data ptr */ + struct { +#define DSP_EXT_RX_CMD_NONE 0 +#define DSP_EXT_RX_CMD_AB 1 +#define DSP_EXT_RX_CMD_NB 2 + uint16_t cmd; + uint16_t data; + } rx[8]; + + /* SCH data */ + uint16_t sch[2]; + + /* Generic data array */ + uint16_t data[0]; +} __attribute__((packed)); + +struct dsp_ext_api { + struct dsp_ext_ndb *ndb; + struct dsp_ext_db *db[2]; +}; + +struct dsp_ext_api dsp_ext_api = { + .ndb = (struct dsp_ext_ndb *) BASE_API_EXT_NDB, + .db = { + (struct dsp_ext_db *) BASE_API_EXT_DB0, + (struct dsp_ext_db *) BASE_API_EXT_DB1, + }, +}; + +static inline struct dsp_ext_db * +dsp_ext_get_db(int r_wn /* 0=W, 1=R */) +{ + int idx = r_wn ? dsp_api.r_page : dsp_api.w_page; + return dsp_ext_api.db[idx]; +} + + + + + +static uint32_t +sb_build(uint8_t bsic, uint16_t t1, uint8_t t2, uint8_t t3p) +{ + return ((t1 & 0x001) << 23) | + ((t1 & 0x1fe) << 7) | + ((t1 & 0x600) >> 9) | + ((t2 & 0x1f) << 18) | + ((t3p & 0x1) << 24) | + ((t3p & 0x6) << 15) | + ((bsic & 0x3f) << 2); +} + + +static int +l1s_bts_resp(uint8_t p1, uint8_t p2, uint16_t p3) +{ + struct dsp_ext_db *db = dsp_ext_get_db(1); + struct gsm_time rx_time; + + /* Time of the command */ + gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 2); + /* We're shifted in time since we RX in the 'next' frame */ + + + /* RX side */ + /* ------- */ + + /* Access Burst ? */ + if (db->rx[0].cmd == DSP_EXT_RX_CMD_AB) + { + static int energy_avg = 0x8000; + + if (db->rx[0].data > (energy_avg << 1)) + { + struct msgb *msg; + struct l1ctl_bts_burst_ab_ind *bi; + uint16_t *iq = &db->data[32]; + int i; + + printf("### RACH ### (%04x %04x - %04x)\n", + db->rx[0].data, energy_avg, db->rx[1].cmd); + + /* Create message */ + msg = l1ctl_msgb_alloc(L1CTL_BTS_BURST_AB_IND); + if (!msg) + goto exit; + + bi = (struct l1ctl_bts_burst_ab_ind *) msgb_put(msg, sizeof(*bi)); + + /* Frame number */ + bi->fn = htonl(rx_time.fn); + + /* Data (cut to 8 bits */ + for (i=0; i<2*88; i++) + bi->iq[i] = iq[i] >> 8; + + /* Send it ! */ + l1_queue_for_l2(msg); + } else + energy_avg = ((energy_avg * 7) + db->rx[0].data) >> 3; + } + + /* Normal Burst ? */ + else if (db->rx[0].cmd == DSP_EXT_RX_CMD_NB) + { + uint16_t *d = &db->data[32]; + + if (d[3] > 0x1000) { + struct msgb *msg; + struct l1ctl_bts_burst_nb_ind *bi; + int i; + + printf("### NB ### (%04x %04x)\n", d[1], d[3]); + + /* Create message */ + msg = l1ctl_msgb_alloc(L1CTL_BTS_BURST_NB_IND); + if (!msg) + goto exit; + + bi = (struct l1ctl_bts_burst_nb_ind *) msgb_put(msg, sizeof(*bi)); + + /* Frame number */ + bi->fn = htonl(rx_time.fn); + + /* Timeslot */ + bi->tn = 0; + + /* TOA */ + bi->toa = d[0]; + + /* Pack bits */ + memset(bi->data, 0x00, sizeof(bi->data)); + + for (i=0; i<116; i++) + { + int sbit = 0x0008 << ((3 - (i & 3)) << 2); + int sword = i >> 2; + int dbit = 1 << (7 - (i & 7)); + int dbyte = i >> 3; + + if (d[5+sword] & sbit) + bi->data[dbyte] |= dbit; + } + + /* Send it ! */ + l1_queue_for_l2(msg); + } + } + +exit: + /* We're done with this */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int +l1s_bts_cmd(uint8_t p1, uint8_t p2, uint16_t p3) +{ + struct dsp_ext_db *db = dsp_ext_get_db(0); + + uint32_t sb; + uint8_t data[15]; + int type, i, t3; + + /* Enable extensions */ + dsp_ext_api.ndb->active = 1; + + /* Force the TSC in all cases */ + dsp_ext_api.ndb->tsc = l1s.bts.bsic & 7; + + + /* RX side */ + /* ------- */ + + db->rx[0].cmd = 0; + db->rx[0].data = 0x000; + + t3 = l1s.next_time.t3; + + if ((t3 < 2) || (t3 > 5)) + { + /* We're really a frame in advance since we RX in the next frame ! */ + t3 = t3 - 1; + + /* Select which type of burst */ + if ((t3 >= 14) && (t3 <= 36)) + db->rx[0].cmd = DSP_EXT_RX_CMD_AB; + else if ((t3 == 45) || (t3 == 46)) + db->rx[0].cmd = DSP_EXT_RX_CMD_AB; + else + db->rx[0].cmd = DSP_EXT_RX_CMD_NB; + + /* Enable dummy bursts detection */ + dsp_api.db_w->d_ctrl_system |= (1 << B_BCCH_FREQ_IND); + + /* Enable task */ + dsp_api.db_w->d_task_d = 23; + + /* Open RX window */ + l1s_rx_win_ctrl(l1s.bts.arfcn | ARFCN_UPLINK, L1_RXWIN_NB, 0); + } + + + /* TX side */ + /* ------- */ + + #define SLOT 3 + + /* Reset all commands to dummy burst */ + for (i=0; i<8; i++) + db->tx[i].cmd = DSP_EXT_TX_CMD_DUMMY; + + /* Get the next burst */ + type = trx_get_burst(l1s.next_time.fn, 0, data); + + /* Program the TX commands */ + switch (type) { + case BURST_FB: + db->tx[SLOT].cmd = DSP_EXT_TX_CMD_FB; + break; + + case BURST_SB: + db->tx[SLOT].cmd = DSP_EXT_TX_CMD_SB; + + sb = sb_build( + l1s.bts.bsic, /* BSIC */ + l1s.next_time.t1, /* T1 */ + l1s.next_time.t2, /* T2 */ + (l1s.next_time.t3 - 1) / 10 /* T3' */ + ); + + db->sch[0] = sb; + db->sch[1] = sb >> 16; + + break; + + case BURST_DUMMY: + db->tx[SLOT].cmd = DSP_EXT_TX_CMD_DUMMY; + break; + + case BURST_NB: + db->tx[SLOT].cmd = DSP_EXT_TX_CMD_NB; + db->tx[SLOT].data = sizeof(struct dsp_ext_db) / sizeof(uint16_t); + + for (i=0; i<7; i++) + db->data[i] = (data[i<<1] << 8) | data[(i<<1)+1]; + db->data[7] = data[14] << 8; + break; + + default: + db->tx[SLOT].cmd = DSP_EXT_TX_CMD_DUMMY; + break; + } + + /* Enable the task */ + dsp_api.db_w->d_task_ra = RACH_DSP_TASK; + + /* Open TX window */ + l1s_tx_apc_helper(l1s.bts.arfcn); + l1s_tx_multi_win_ctrl(l1s.bts.arfcn, 0, 2, 5); + + return 0; +} + +const struct tdma_sched_item bts_sched_set[] = { + SCHED_ITEM_DT(l1s_bts_cmd, 2, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_bts_resp, -5, 0, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; -- cgit v1.2.3