diff options
Diffstat (limited to 'src')
35 files changed, 5759 insertions, 4023 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6646f534..beee4c09 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,14 +34,22 @@ libgprs_la_SOURCES = \ gsm_rlcmac.cpp \ gprs_bssgp_pcu.cpp \ gprs_rlcmac.cpp \ - gprs_rlcmac_data.cpp \ gprs_rlcmac_sched.cpp \ gprs_rlcmac_meas.cpp \ + gprs_rlcmac_ts_alloc.cpp \ gsm_timer.cpp \ bitvector.cpp \ pcu_l1_if.cpp \ pcu_vty.c \ - tbf.cpp + tbf.cpp \ + bts.cpp \ + poll_controller.cpp \ + encoding.cpp \ + ta.cpp \ + sba.cpp \ + decoding.cpp \ + llc.cpp \ + rlc.cpp if ENABLE_SYSMOBTS libgprs_la_SOURCES += \ @@ -74,7 +82,15 @@ noinst_HEADERS = \ pcu_vty.h \ sysmo_l1_if.h \ femtobts.h \ - tbf.h + tbf.h \ + bts.h \ + poll_controller.h \ + encoding.h \ + ta.h \ + sba.h \ + rlc.h \ + decoding.h \ + llc.h osmo_pcu_SOURCES = pcu_main.cpp diff --git a/src/bts.cpp b/src/bts.cpp new file mode 100644 index 00000000..ff16e296 --- /dev/null +++ b/src/bts.cpp @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2013 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 Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <bts.h> +#include <poll_controller.h> +#include <tbf.h> +#include <encoding.h> +#include <decoding.h> +#include <rlc.h> +#include <pcu_l1_if.h> + +#include <gprs_rlcmac.h> +#include <gprs_debug.h> + +extern "C" { + #include <osmocom/core/talloc.h> + #include <osmocom/core/msgb.h> +} + +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> + +extern void *tall_pcu_ctx; + +static BTS s_bts; + +/** + * For gcc-4.4 compat do not use extended initializer list but keep the + * order from the enum here. Once we support GCC4.7 and up we can change + * the code below. + */ +static const struct rate_ctr_desc bts_ctr_description[] = { + { "tbf.dl.alloc", "TBF DL Allocated "}, + { "tbf.dl.freed", "TBF DL Freed "}, + { "tbf.ul.alloc", "TBF UL Allocated "}, + { "tbf.ul.freed", "TBF UL Freed "}, + { "tbf.reused", "TBF Reused "}, + { "rlc.sent", "RLC Sent "}, + { "rlc.resent", "RLC Resent "}, + { "rlc.restarted", "RLC Restarted "}, + { "rlc.stalled", "RLC Stalled "}, + { "rlc.nacked", "RLC Nacked "}, + { "decode.errors", "Decode Errors "}, + { "sba.allocated", "SBA Allocated "}, + { "sba.freed", "SBA Freed "}, + { "sba.timedout", "SBA Timeout "}, + { "llc.timeout", "Timedout Frames "}, + { "llc.dropped", "Dropped Frames "}, + { "llc.scheduled", "Scheduled Frames "}, + { "rach.requests", "RACH requests "}, +}; + +static const struct rate_ctr_group_desc bts_ctrg_desc = { + "bts", + "BTS Statistics", + ARRAY_SIZE(bts_ctr_description), + bts_ctr_description, +}; + +BTS* BTS::main_bts() +{ + return &s_bts; +} + +struct gprs_rlcmac_bts *BTS::bts_data() +{ + return &m_bts; +} + +struct gprs_rlcmac_bts *bts_main_data() +{ + return BTS::main_bts()->bts_data(); +} + +struct rate_ctr_group *bts_main_data_stats() +{ + return BTS::main_bts()->rate_counters(); +} + +BTS::BTS() + : m_cur_fn(0) + , m_pollController(*this) + , m_sba(*this) +{ + memset(&m_bts, 0, sizeof(m_bts)); + INIT_LLIST_HEAD(&m_bts.ul_tbfs); + INIT_LLIST_HEAD(&m_bts.dl_tbfs); + m_bts.bts = this; + + /* initialize back pointers */ + for (size_t trx_no = 0; trx_no < ARRAY_SIZE(m_bts.trx); ++trx_no) { + struct gprs_rlcmac_trx *trx = &m_bts.trx[trx_no]; + trx->trx_no = trx_no; + trx->bts = this; + + for (size_t ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) { + struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no]; + pdch->ts_no = ts_no; + pdch->trx = trx; + } + } + + m_ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 0); +} + +BTS::~BTS() +{ + rate_ctr_group_free(m_ratectrs); +} + + +void BTS::set_current_frame_number(int fn) +{ + m_cur_fn = fn; + m_pollController.expireTimedout(m_cur_fn); +} + +int BTS::add_paging(uint8_t chan_needed, uint8_t *identity_lv) +{ + uint8_t l, trx, ts, any_tbf = 0; + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_paging *pag; + uint8_t slot_mask[8]; + int8_t first_ts; /* must be signed */ + + llist_head *tbfs_lists[] = { + &m_bts.ul_tbfs, + &m_bts.dl_tbfs, + NULL + }; + + + LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n", + chan_needed, osmo_hexdump(identity_lv + 1, identity_lv[0])); + + /* collect slots to page + * Mark slots for every TBF, but only mark one of it. + * Mark only the first slot found. + * Don't mark, if TBF uses a different slot that is already marked. */ + memset(slot_mask, 0, sizeof(slot_mask)); + for (l = 0; tbfs_lists[l]; l++) { + llist_for_each_entry(tbf, tbfs_lists[l], list) { + first_ts = -1; + for (ts = 0; ts < 8; ts++) { + if (tbf->pdch[ts]) { + /* remember the first slot found */ + if (first_ts < 0) + first_ts = ts; + /* break, if we already marked a slot */ + if ((slot_mask[tbf->trx->trx_no] & (1 << ts))) + break; + } + } + /* mark first slot found, if none is marked already */ + if (ts == 8 && first_ts >= 0) { + LOGP(DRLCMAC, LOGL_DEBUG, "- %s uses " + "TRX=%d TS=%d, so we mark\n", + tbf_name(tbf), + tbf->trx->trx_no, first_ts); + slot_mask[tbf->trx->trx_no] |= (1 << first_ts); + } else + LOGP(DRLCMAC, LOGL_DEBUG, "- %s uses " + "already marked TRX=%d TS=%d\n", + tbf_name(tbf), + tbf->trx->trx_no, ts); + } + } + + /* Now we have a list of marked slots. Every TBF uses at least one + * of these slots. */ + + /* schedule paging to all marked slots */ + for (trx = 0; trx < 8; trx++) { + if (slot_mask[trx] == 0) + continue; + for (ts = 0; ts < 8; ts++) { + if ((slot_mask[trx] & (1 << ts))) { + /* schedule */ + pag = talloc_zero(tall_pcu_ctx, + struct gprs_rlcmac_paging); + if (!pag) + return -ENOMEM; + pag->chan_needed = chan_needed; + memcpy(pag->identity_lv, identity_lv, + identity_lv[0] + 1); + m_bts.trx[trx].pdch[ts].add_paging(pag); + LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of " + "TRX=%d TS=%d\n", trx, ts); + any_tbf = 1; + } + } + } + + if (!any_tbf) + LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n"); + + return 0; +} + +/* search for active downlink or uplink tbf */ +gprs_rlcmac_tbf *BTS::tbf_by_tlli(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir) +{ + struct gprs_rlcmac_tbf *tbf; + if (dir == GPRS_RLCMAC_UL_TBF) { + llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) { + if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) + && tbf->tlli() == tlli && tbf->is_tlli_valid()) + return tbf; + } + } else { + llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) { + if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) + && tbf->tlli() == tlli) + return tbf; + } + } + return NULL; +} + +gprs_rlcmac_tbf *BTS::tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts) +{ + struct gprs_rlcmac_tbf *tbf; + + /* only one TBF can poll on specific TS/FN, because scheduler can only + * schedule one downlink control block (with polling) at a FN per TS */ + llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) { + if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) + && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED + && tbf->poll_fn == fn && tbf->trx->trx_no == trx + && tbf->control_ts == ts) + return tbf; + } + llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) { + if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) + && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED + && tbf->poll_fn == fn && tbf->trx->trx_no == trx + && tbf->control_ts == ts) + return tbf; + } + return NULL; +} + +/* lookup TBF Entity (by TFI) */ +gprs_rlcmac_tbf *BTS::tbf_by_tfi(uint8_t tfi, uint8_t trx, + enum gprs_rlcmac_tbf_direction dir) +{ + struct gprs_rlcmac_tbf *tbf; + + if (tfi >= 32 || trx >= 8) + return NULL; + + if (dir == GPRS_RLCMAC_UL_TBF) + tbf = m_bts.trx[trx].ul_tbf[tfi]; + else + tbf = m_bts.trx[trx].dl_tbf[tfi]; + if (!tbf) + return NULL; + + if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) + return tbf; + + return NULL; +} + +/* FIXME: spread resources over multiple TRX. Also add option to use same + * TRX in case of existing TBF for TLLI in the other direction. */ +/* search for free TFI and return TFI, TRX */ +int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir, + uint8_t *_trx, int8_t use_trx) +{ + struct gprs_rlcmac_pdch *pdch; + struct gprs_rlcmac_tbf **tbfp; + uint8_t trx_from, trx_to, trx, ts, tfi; + + if (use_trx >= 0 && use_trx < 8) + trx_from = trx_to = use_trx; + else { + trx_from = 0; + trx_to = 7; + } + + /* on TRX find first enabled TS */ + for (trx = trx_from; trx <= trx_to; trx++) { + for (ts = 0; ts < 8; ts++) { + pdch = &m_bts.trx[trx].pdch[ts]; + if (!pdch->is_enabled()) + continue; + break; + } + if (ts < 8) + break; + } + if (trx > trx_to) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n"); + return -EINVAL; + } + + + LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: " + "TRX=%d first TS=%d\n", trx, ts); + if (dir == GPRS_RLCMAC_UL_TBF) + tbfp = m_bts.trx[trx].ul_tbf; + else + tbfp = m_bts.trx[trx].dl_tbf; + for (tfi = 0; tfi < 32; tfi++) { + if (!tbfp[tfi]) + break; + } + + if (tfi < 32) { + LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi); + *_trx = trx; + return tfi; + } + LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n"); + + return -1; +} + +int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn) +{ + struct gprs_rlcmac_tbf *tbf; + uint8_t plen; + uint32_t tlli; + + /* move to IA Rest Octets */ + plen = data[0] >> 2; + data += 1 + plen; + + if ((*data & 0xf0) != 0xd0) { + LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest " + "octets do not start with bit sequence 'HH01' " + "(Packet Downlink Assignment)\n"); + return -EINVAL; + } + + /* get TLLI from downlink assignment */ + tlli = (*data++) << 28; + tlli |= (*data++) << 20; + tlli |= (*data++) << 12; + tlli |= (*data++) << 4; + tlli |= (*data++) >> 4; + + tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF); + if (!tbf) { + LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x " + "does not exit\n", tlli); + return -EINVAL; + } + + LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli); + + if (tbf->dir.dl.wait_confirm) + tbf_timer_start(tbf, 0, Tassign_agch); + + return 0; +} + +int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta) +{ + struct gprs_rlcmac_tbf *tbf; + uint8_t trx_no, ts_no = 0; + int8_t tfi; /* must be signed */ + uint8_t sb = 0; + uint32_t sb_fn = 0; + int rc; + uint8_t plen; + + rach_frame(); + + LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide " + "one:\n"); + if ((ra & 0xf8) == 0x70) { + LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block " + "allocation\n"); + sb = 1; + } else if (m_bts.force_two_phase) { + LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, " + "but we force two phase access\n"); + sb = 1; + } + if (qta < 0) + qta = 0; + if (qta > 252) + qta = 252; + if (sb) { + rc = sba()->alloc(&trx_no, &ts_no, &sb_fn, qta >> 2); + if (rc < 0) + return rc; + LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d " + "ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn, + (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); + LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink " + "(AGCH)\n"); + } else { + // Create new TBF + #warning "Copy and pate with other routines.." + tfi = tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1); + if (tfi < 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); + /* FIXME: send reject */ + return -EBUSY; + } + /* set class to 0, since we don't know the multislot class yet */ + tbf = tbf_alloc(&m_bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx_no, 0, 1); + if (!tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); + /* FIXME: send reject */ + return -EBUSY; + } + tbf->ta = qta >> 2; + tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); + tbf_timer_start(tbf, 3169, m_bts.t3169, 0); + LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n", + tbf_name(tbf)); + LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH " + "qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n", + tbf_name(tbf), + qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); + LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate " + "Assignment Uplink (AGCH)\n", tbf_name(tbf)); + } + bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */; + bitvec_unhex(immediate_assignment, + "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + if (sb) + plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 0, ra, + Fn, qta >> 2, m_bts.trx[trx_no].arfcn, ts_no, + m_bts.trx[trx_no].pdch[ts_no].tsc, 0, 0, 0, 0, sb_fn, 1, + m_bts.alpha, m_bts.gamma, -1); + else + plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 0, ra, + Fn, tbf->ta, tbf->trx->arfcn, tbf->first_ts, tbf->tsc(), + tbf->tfi(), tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0, + m_bts.alpha, m_bts.gamma, -1); + pcu_l1if_tx_agch(immediate_assignment, plen); + bitvec_free(immediate_assignment); + + return 0; +} + +/* depending on the current TBF, we assign on PACCH or AGCH */ +void BTS::trigger_dl_ass( + struct gprs_rlcmac_tbf *tbf, + struct gprs_rlcmac_tbf *old_tbf, const char *imsi) +{ + /* stop pending timer */ + tbf->stop_timer(); + + /* check for downlink tbf: */ + if (old_tbf) { + LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on " + "PACCH, because %s exists\n", tbf_name(old_tbf)); + old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; + /* use TA from old TBF */ + tbf->ta = old_tbf->ta; + /* change state */ + tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); + tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); + /* start timer */ + tbf_timer_start(tbf, 0, Tassign_pacch); + } else { + LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for %s on PCH, no TBF exist (IMSI=%s)\n", tbf_name(tbf), imsi); + if (!imsi || strlen(imsi) < 3) { + LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n"); + return; + } + /* change state */ + tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); + tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); + tbf->assign_imsi(imsi); + /* send immediate assignment */ + tbf->bts->snd_dl_ass(tbf, 0, imsi); + tbf->dir.dl.wait_confirm = 1; + } +} + +void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi) +{ + int plen; + + LOGP(DRLCMAC, LOGL_INFO, "TX: START %s Immediate Assignment Downlink (PCH)\n", tbf_name(tbf)); + bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */ + bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + /* use request reference that has maximum distance to current time, + * so the assignment will not conflict with possible RACH requests. */ + plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 1, 125, + (tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta, + tbf->trx->arfcn, tbf->first_ts, tbf->tsc(), tbf->tfi(), 0, tbf->tlli(), poll, + tbf->poll_fn, 0, m_bts.alpha, m_bts.gamma, -1); + pcu_l1if_tx_pch(immediate_assignment, plen, imsi); + bitvec_free(immediate_assignment); +} + + +/* + * PDCH code below. TODO: move to a separate file + */ + +void gprs_rlcmac_pdch::enable() +{ + /* TODO: Check if there are still allocated resources.. */ + INIT_LLIST_HEAD(&paging_list); + m_is_enabled = 1; +} + +void gprs_rlcmac_pdch::disable() +{ + /* TODO.. kick free_resources once we know the TRX/TS we are on */ + m_is_enabled = 0; +} + +/* TODO: kill the parameter and make a pdch belong to a trx.. to a bts.. */ +void gprs_rlcmac_pdch::free_resources() +{ + struct gprs_rlcmac_paging *pag; + + /* we are not enabled. there should be no resources */ + if (!is_enabled()) + return; + + /* kick all TBF on slot */ + gprs_rlcmac_tbf::free_all(this); + + /* flush all pending paging messages */ + while ((pag = dequeue_paging())) + talloc_free(pag); + + trx->bts->sba()->free_resources(this); +} + +struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging() +{ + struct gprs_rlcmac_paging *pag; + + if (llist_empty(&paging_list)) + return NULL; + pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list); + llist_del(&pag->list); + + return pag; +} + +struct msgb *gprs_rlcmac_pdch::packet_paging_request() +{ + struct gprs_rlcmac_paging *pag; + struct msgb *msg; + unsigned wp = 0, len; + + /* no paging, no message */ + pag = dequeue_paging(); + if (!pag) + return NULL; + + LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n"); + + /* alloc message */ + msg = msgb_alloc(23, "pag ctrl block"); + if (!msg) { + talloc_free(pag); + return NULL; + } + bitvec *pag_vec = bitvec_alloc(23); + if (!pag_vec) { + msgb_free(msg); + talloc_free(pag); + return NULL; + } + wp = Encoding::write_packet_paging_request(pag_vec); + + /* loop until message is full */ + while (pag) { + /* try to add paging */ + if ((pag->identity_lv[1] & 0x07) == 4) { + /* TMSI */ + LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n", + ntohl(*((uint32_t *)(pag->identity_lv + 1)))); + len = 1 + 1 + 1 + 32 + 2 + 1; + if (pag->identity_lv[0] != 5) { + LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with " + "MI != 5 octets!\n"); + goto continue_next; + } + } else { + /* MI */ + LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n", + osmo_hexdump(pag->identity_lv + 1, + pag->identity_lv[0])); + len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1; + if (pag->identity_lv[0] > 8) { + LOGP(DRLCMAC, LOGL_ERROR, "Paging with " + "MI > 8 octets!\n"); + goto continue_next; + } + } + if (wp + len > 184) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule " + "next time\n"); + /* put back paging record, because does not fit */ + llist_add_tail(&pag->list, &paging_list); + break; + } + Encoding::write_repeated_page_info(pag_vec, wp, pag->identity_lv[0], + pag->identity_lv + 1, pag->chan_needed); + +continue_next: + talloc_free(pag); + pag = dequeue_paging(); + } + + bitvec_pack(pag_vec, msgb_put(msg, 23)); + RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); + LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n"); + decode_gsm_rlcmac_downlink(pag_vec, mac_control_block); + LOGPC(DCSN1, LOGL_NOTICE, "\n"); + LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n"); + bitvec_free(pag_vec); + talloc_free(mac_control_block); + + return msg; +} + +void gprs_rlcmac_pdch::add_paging(struct gprs_rlcmac_paging *pag) +{ + llist_add(&pag->list, &paging_list); +} + +/* receive UL data block + * + * The blocks are defragmented and forwarded as LLC frames, if complete. + */ +int gprs_rlcmac_pdch::rcv_data_block_acknowledged(uint8_t *data, uint8_t len, int8_t rssi) +{ + struct gprs_rlcmac_tbf *tbf; + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + + switch (len) { + case 54: + /* omitting spare bits */ + len = 53; + break; + case 40: + /* omitting spare bits */ + len = 39; + break; + case 34: + /* omitting spare bits */ + len = 33; + break; + case 23: + break; + default: + bts()->decode_error(); + LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid" + "length: %d)\n", len); + return -EINVAL; + } + + /* find TBF inst from given TFI */ + tbf = bts()->tbf_by_tfi(rh->tfi, trx_no(), GPRS_RLCMAC_UL_TBF); + if (!tbf) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TFI=%d\n", + rh->tfi); + return 0; + } + + return tbf->rcv_data_block_acknowledged(data, len, rssi); +} + +void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn) +{ + struct gprs_rlcmac_tbf *tbf; + uint32_t tlli = 0; + + tlli = packet->TLLI; + tbf = bts()->tbf_by_poll_fn(fn, trx_no(), ts_no); + if (!tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with " + "unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n", + fn, tlli, trx_no(), ts_no); + return; + } + tbf->update_tlli(tlli); + LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Control Ack\n", tbf_name(tbf)); + tbf->poll_state = GPRS_RLCMAC_POLL_NONE; + + /* check if this control ack belongs to packet uplink ack */ + if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { + LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] END %s\n", tbf_name(tbf)); + tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; + if ((tbf->state_flags & + (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) { + tbf->state_flags &= + ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ACK); + LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink " + "ack for UL %s\n", tbf_name(tbf)); + } + tbf_free(tbf); + return; + } + if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) { + LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] DOWNLINK ASSIGNED %s\n", tbf_name(tbf)); + /* reset N3105 */ + tbf->n3105 = 0; + tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; + if (tbf->direction == GPRS_RLCMAC_UL_TBF) + tbf = bts()->tbf_by_tlli(tbf->tlli(), + GPRS_RLCMAC_DL_TBF); +#warning "TBF is changing on the way... *sigh*" + if (!tbf) { + LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL " + "TBF is gone TLLI=0x%08x\n", tlli); + return; + } + tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + /* stop pending assignment timer */ + tbf->stop_timer(); + if ((tbf->state_flags & + (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) { + tbf->state_flags &= + ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS); + LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink " + "assignment for %s\n", tbf_name(tbf)); + } + tbf_assign_control_ts(tbf); + return; + } + if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) { + LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] UPLINK ASSIGNED %s\n", tbf_name(tbf)); + /* reset N3105 */ + tbf->n3105 = 0; + tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; +#warning "TBF is changing on the way... *sigh*" + if (tbf->direction == GPRS_RLCMAC_DL_TBF) + tbf = bts()->tbf_by_tlli(tbf->tlli(), + GPRS_RLCMAC_UL_TBF); + if (!tbf) { + LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL " + "TBF is gone TLLI=0x%08x\n", tlli); + return; + } + tbf_new_state(tbf, GPRS_RLCMAC_FLOW); + if ((tbf->state_flags & + (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { + tbf->state_flags &= + ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS); + LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink " + "assignment for UL %s\n", tbf_name(tbf)); + } + tbf_assign_control_ts(tbf); + return; + } + LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK " + "at no request\n"); +} + +void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn) +{ + int8_t tfi = 0; /* must be signed */ + struct gprs_rlcmac_tbf *tbf; + int rc; + + tfi = ack_nack->DOWNLINK_TFI; + tbf = bts()->tbf_by_poll_fn(fn, trx_no(), ts_no); + if (!tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with " + "unknown FN=%u TFI=%d (TRX %d TS %d)\n", + fn, tfi, trx_no(), ts_no); + return; + } + if (tbf->tfi() != tfi) { + LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with " + "wrong TFI=%d, ignoring!\n", tfi); + return; + } + tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK); + if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) { + tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); + LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink ack " + "for %s\n", tbf_name(tbf)); + } + /* reset N3105 */ + tbf->n3105 = 0; + tbf->stop_t3191(); + LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Downlink Ack/Nack\n", tbf_name(tbf)); + tbf->poll_state = GPRS_RLCMAC_POLL_NONE; + + rc = tbf->snd_dl_ack( + ack_nack->Ack_Nack_Description.FINAL_ACK_INDICATION, + ack_nack->Ack_Nack_Description.STARTING_SEQUENCE_NUMBER, + ack_nack->Ack_Nack_Description.RECEIVED_BLOCK_BITMAP); + if (rc == 1) { + tbf_free(tbf); + return; + } + /* check for channel request */ + if (ack_nack->Exist_Channel_Request_Description) { + LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack " + "message, so we provide one:\n"); + tbf_alloc_ul(bts_data(), tbf->trx->trx_no, tbf->ms_class, tbf->tlli(), tbf->ta, tbf); + /* schedule uplink assignment */ + tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; + } +} + +void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn) +{ + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_sba *sba; + int rc; + + if (request->ID.UnionType) { + uint32_t tlli = request->ID.u.TLLI; + uint8_t ms_class = 0; + struct gprs_rlcmac_tbf *dl_tbf; + uint8_t ta; + + tbf = bts()->tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF); + if (tbf) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while %s still " + "exists. Killing pending DL TBF\n", + tlli, tbf_name(tbf)); + tbf_free(tbf); + tbf = NULL; + } + + if ((dl_tbf = bts()->tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while %s still exists. " + "Killing pending DL TBF\n", tlli, + tbf_name(dl_tbf)); + tbf_free(dl_tbf); + dl_tbf = NULL; + } + LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF " + "in packet resource request of single " + "block, so we provide one:\n"); + sba = bts()->sba()->find(this, fn); + if (!sba) { + LOGP(DRLCMAC, LOGL_NOTICE, "MS requests UL TBF " + "in packet resource request of single " + "block, but there is no resource request " + "scheduled!\n"); + rc = bts()->timing_advance()->recall(tlli); + if (rc >= 0) + ta = rc; + else + ta = 0; + } else { + ta = sba->ta; + bts()->timing_advance()->remember(tlli, ta); + bts()->sba()->free_sba(sba); + } + if (request->Exist_MS_Radio_Access_capability) + ms_class = Decoding::get_ms_class_by_capability(&request->MS_Radio_Access_capability); + if (!ms_class) + LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n"); + tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class, tlli, ta, NULL); + if (!tbf) + return; + /* set control ts to current MS's TS, until assignment complete */ + LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts_no); + tbf->control_ts = ts_no; + /* schedule uplink assignment */ + tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; + return; + } + + if (request->ID.u.Global_TFI.UnionType) { + int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI; + tbf = bts()->tbf_by_tfi(tfi, trx_no(), GPRS_RLCMAC_DL_TBF); + if (!tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TFI=%d\n", tfi); + return; + } + } else { + int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI; + tbf = bts()->tbf_by_tfi(tfi, trx_no(), GPRS_RLCMAC_UL_TBF); + if (!tbf) { + LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TFI=%d\n", tfi); + return; + } + } + + LOGP(DRLCMAC, LOGL_ERROR, "RX: [PCU <- BTS] %s FIXME: Packet resource request\n", tbf_name(tbf)); +} + +void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn) +{ + struct gprs_rlcmac_sba *sba; + + sba = bts()->sba()->find(this, fn); + if (!sba) { + LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement " + "in packet resource request of single " + "block, but there is no resource request " + "scheduled! TLLI=0x%08x\n", report->TLLI); + } else { + bts()->timing_advance()->remember(report->TLLI, sba->ta); + bts()->sba()->free_sba(sba); + } + gprs_rlcmac_meas_rep(report); +} + +/* Received Uplink RLC control block. */ +int gprs_rlcmac_pdch::rcv_control_block( + bitvec *rlc_block, uint32_t fn) +{ + RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t); + LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n"); + decode_gsm_rlcmac_uplink(rlc_block, ul_control_block); + LOGPC(DCSN1, LOGL_NOTICE, "\n"); + LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n"); + switch (ul_control_block->u.MESSAGE_TYPE) { + case MT_PACKET_CONTROL_ACK: + rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn); + break; + case MT_PACKET_DOWNLINK_ACK_NACK: + rcv_control_dl_ack_nack(&ul_control_block->u.Packet_Downlink_Ack_Nack, fn); + break; + case MT_PACKET_RESOURCE_REQUEST: + rcv_resource_request(&ul_control_block->u.Packet_Resource_Request, fn); + break; + case MT_PACKET_MEASUREMENT_REPORT: + rcv_measurement_report(&ul_control_block->u.Packet_Measurement_Report, fn); + break; + case MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK: + /* ignoring it. change the SI to not force sending these? */ + break; + default: + bts()->decode_error(); + LOGP(DRLCMAC, LOGL_NOTICE, + "RX: [PCU <- BTS] unknown control block(%d) received\n", + ul_control_block->u.MESSAGE_TYPE); + } + talloc_free(ul_control_block); + return 1; +} + + +/* received RLC/MAC block from L1 */ +int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi) +{ + unsigned payload = data[0] >> 6; + bitvec *block; + int rc = 0; + + switch (payload) { + case GPRS_RLCMAC_DATA_BLOCK: + rc = rcv_data_block_acknowledged(data, len, rssi); + break; + case GPRS_RLCMAC_CONTROL_BLOCK: + block = bitvec_alloc(len); + if (!block) + return -ENOMEM; + bitvec_unpack(block, data); + rc = rcv_control_block(block, fn); + bitvec_free(block); + break; + case GPRS_RLCMAC_CONTROL_BLOCK_OPT: + LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n"); + break; + default: + LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload); + rc = -EINVAL; + } + + return rc; +} diff --git a/src/bts.h b/src/bts.h new file mode 100644 index 00000000..128b1e0a --- /dev/null +++ b/src/bts.h @@ -0,0 +1,321 @@ +/* bts.h + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + + +#ifdef __cplusplus +extern "C" { +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/timer.h> +} + +#include "poll_controller.h" +#include "sba.h" +#include "ta.h" +#include "tbf.h" +#endif + +#include <stdint.h> + +struct BTS; + +/* + * PDCH instance + */ +struct gprs_rlcmac_pdch { +#ifdef __cplusplus + struct gprs_rlcmac_paging *dequeue_paging(); + struct msgb *packet_paging_request(); + + void add_paging(struct gprs_rlcmac_paging *pag); + + void free_resources(); + + bool is_enabled() const; + + void enable(); + void disable(); + + /* dispatching of messages */ + int rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi); + + gprs_rlcmac_bts *bts_data() const; + BTS *bts() const; + uint8_t trx_no() const; +#endif + + uint8_t m_is_enabled; /* TS is enabled */ + uint8_t tsc; /* TSC of this slot */ + uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */ + uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */ + uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */ + struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */ + struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */ + struct llist_head paging_list; /* list of paging messages */ + uint32_t last_rts_fn; /* store last frame number of RTS */ + + /* back pointers */ + struct gprs_rlcmac_trx *trx; + uint8_t ts_no; + +#ifdef __cplusplus +private: + int rcv_data_block_acknowledged(uint8_t *data, uint8_t len, int8_t rssi); + int rcv_control_block(bitvec *rlc_block, uint32_t fn); + + void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn); + void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn); + void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn); + void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn); +#endif +}; + +struct gprs_rlcmac_trx { + void *fl1h; + uint16_t arfcn; + struct gprs_rlcmac_pdch pdch[8]; + struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */ + struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */ + + /* back pointers */ + struct BTS *bts; + uint8_t trx_no; +}; + +/** + * This is the data from C. As soon as our minimal compiler is gcc 4.7 + * we can start to compile pcu_vty.c with c++ and remove the split. + */ +struct gprs_rlcmac_bts { + uint8_t bsic; + uint8_t fc_interval; + uint8_t cs1; + uint8_t cs2; + uint8_t cs3; + uint8_t cs4; + uint8_t initial_cs_dl, initial_cs_ul; + uint8_t force_cs; /* 0=use from BTS 1=use from VTY */ + uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */ + uint8_t t3142; + uint8_t t3169; + uint8_t t3191; + uint16_t t3193_msec; + uint8_t t3195; + uint8_t n3101; + uint8_t n3103; + uint8_t n3105; + struct gprs_rlcmac_trx trx[8]; + int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, + struct gprs_rlcmac_tbf *old_tbf, + struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single); + uint32_t alloc_algorithm_curst; /* options to customize algorithm */ + uint8_t force_two_phase; + uint8_t alpha, gamma; + + /* TBF handling, make private or move into TBFController */ + /* list of uplink TBFs */ + struct llist_head ul_tbfs; + /* list of downlink TBFs */ + struct llist_head dl_tbfs; + + + /** + * Point back to the C++ object. This is used during the transition + * period. + */ + struct BTS *bts; +}; + +#ifdef __cplusplus +/** + * I represent a GSM BTS. I have one or more TRX, I know the current + * GSM time and I have controllers that help with allocating resources + * on my TRXs. + */ +struct BTS { +public: + enum { + CTR_TBF_DL_ALLOCATED, + CTR_TBF_DL_FREED, + CTR_TBF_UL_ALLOCATED, + CTR_TBF_UL_FREED, + CTR_TBF_REUSED, + CTR_RLC_SENT, + CTR_RLC_RESENT, + CTR_RLC_RESTARTED, + CTR_RLC_STALLED, + CTR_RLC_NACKED, + CTR_DECODE_ERRORS, + CTR_SBA_ALLOCATED, + CTR_SBA_FREED, + CTR_SBA_TIMEDOUT, + CTR_LLC_FRAME_TIMEDOUT, + CTR_LLC_FRAME_DROPPED, + CTR_LLC_FRAME_SCHED, + CTR_RACH_REQUESTS, + }; + + BTS(); + ~BTS(); + + static BTS* main_bts(); + + struct gprs_rlcmac_bts *bts_data(); + SBAController *sba(); + TimingAdvance *timing_advance(); + + /** TODO: change the number to unsigned */ + void set_current_frame_number(int frame_number); + int current_frame_number() const; + + /** add paging to paging queue(s) */ + int add_paging(uint8_t chan_needed, uint8_t *identity_lv); + + gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir); + gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts); + gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, enum gprs_rlcmac_tbf_direction dir); + + int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx); + + int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn); + int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta); + + void trigger_dl_ass(gprs_rlcmac_tbf *tbf, gprs_rlcmac_tbf *old_tbf, const char *imsi); + void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi); + + /* + * Statistics + */ + void tbf_dl_created(); + void tbf_dl_freed(); + void tbf_ul_created(); + void tbf_ul_freed(); + void tbf_reused(); + void rlc_sent(); + void rlc_resent(); + void rlc_restarted(); + void rlc_stalled(); + void rlc_nacked(); + void decode_error(); + void sba_allocated(); + void sba_freed(); + void sba_timedout(); + void llc_timedout_frame(); + void llc_dropped_frame(); + void llc_frame_sched(); + void rach_frame(); + + /* + * Below for C interface for the VTY + */ + struct rate_ctr_group *rate_counters() const; + +private: + int m_cur_fn; + struct gprs_rlcmac_bts m_bts; + PollController m_pollController; + SBAController m_sba; + TimingAdvance m_ta; + struct rate_ctr_group *m_ratectrs; + +private: + /* disable copying to avoid slicing */ + BTS(const BTS&); + BTS& operator=(const BTS&); +}; + +inline int BTS::current_frame_number() const +{ + return m_cur_fn; +} + +inline TimingAdvance *BTS::timing_advance() +{ + return &m_ta; +} + +inline SBAController *BTS::sba() +{ + return &m_sba; +} + +inline BTS *gprs_rlcmac_pdch::bts() const +{ + return trx->bts; +} + +inline struct rate_ctr_group *BTS::rate_counters() const +{ + return m_ratectrs; +} + +#define CREATE_COUNT_INLINE(func_name, ctr_name) \ + inline void BTS::func_name() {\ + rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \ + } + +CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED) +CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED) +CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED) +CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED) +CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED) +CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT) +CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT) +CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED) +CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED) +CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED) +CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS) +CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED) +CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED) +CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT) +CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT); +CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED); +CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED); +CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS); + +#undef CREATE_COUNT_INLINE + + +inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const +{ + return trx->bts->bts_data(); +} + +inline uint8_t gprs_rlcmac_pdch::trx_no() const +{ + return trx->trx_no; +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + struct gprs_rlcmac_bts *bts_main_data(); + struct rate_ctr_group *bts_main_data_stats(); +#ifdef __cplusplus +} + +inline bool gprs_rlcmac_pdch::is_enabled() const +{ + return m_is_enabled; +} +#endif diff --git a/src/decoding.cpp b/src/decoding.cpp new file mode 100644 index 00000000..fce8124f --- /dev/null +++ b/src/decoding.cpp @@ -0,0 +1,101 @@ +/* decoding + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <decoding.h> +#include <rlc.h> +#include <gprs_debug.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> + + +int Decoding::tlli_from_ul_data(const uint8_t *data, uint8_t len, + uint32_t *tlli) +{ + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + struct rlc_li_field *li; + uint8_t e; + uint32_t _tlli; + + if (!rh->ti) + return -EINVAL; + + data += 3; + len -= 3; + e = rh->e; + /* if E is not set (LI follows) */ + while (!e) { + if (!len) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " + "but no more data\n"); + return -EINVAL; + } + /* get new E */ + li = (struct rlc_li_field *)data; + if (li->e == 0) /* if LI==0, E is interpreted as '1' */ + e = 1; + else + e = li->e; + data++; + len--; + } + if (len < 4) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame " + "border\n"); + return -EINVAL; + } + memcpy(&_tlli, data, 4); + *tlli = ntohl(_tlli); + + return 0; +} + +uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap) +{ + int i; + + for (i = 0; i < cap->Count_MS_RA_capability_value; i++) { + if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability) + continue; + if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class) + continue; + return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class; + } + + return 0; +} + +/** + * show_rbb needs to be an array with 65 elements + * The index of the array is the bit position in the rbb + * (show_rbb[63] relates to BSN ssn-1) + */ +void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb) +{ + for (int i = 0; i < 64; i++) { + uint8_t bit; + + bit = !!(rbb[i/8] & (1<<(7-i%8))); + show_rbb[i] = bit ? 'R' : 'I'; + } + + show_rbb[64] = '\0'; +} diff --git a/src/decoding.h b/src/decoding.h new file mode 100644 index 00000000..31be0320 --- /dev/null +++ b/src/decoding.h @@ -0,0 +1,33 @@ +/* decoding + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#pragma once + +#include <gsm_rlcmac.h> + +#include <stdint.h> + +class Decoding { +public: + static int tlli_from_ul_data(const uint8_t *data, uint8_t len, + uint32_t *tlli); + static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap); + + static void extract_rbb(const uint8_t *rbb, char *extracted_rbb); +}; diff --git a/src/encoding.cpp b/src/encoding.cpp new file mode 100644 index 00000000..51b0fbe5 --- /dev/null +++ b/src/encoding.cpp @@ -0,0 +1,463 @@ +/* encoding.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <encoding.h> +#include <gprs_rlcmac.h> +#include <bts.h> +#include <tbf.h> +#include <gprs_debug.h> + +// GSM 04.08 9.1.18 Immediate assignment +int Encoding::write_immediate_assignment( + struct gprs_rlcmac_bts *bts, + bitvec * dest, uint8_t downlink, uint8_t ra, + uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, + uint8_t tfi, uint8_t usf, uint32_t tlli, + uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha, + uint8_t gamma, int8_t ta_idx) +{ + unsigned wp = 0; + uint8_t plen; + + bitvec_write_field(dest, wp,0x0,4); // Skip Indicator + bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator + bitvec_write_field(dest, wp,0x3F,8); // Immediate Assignment Message Type + + // 10.5.2.25b Dedicated mode or TBF + bitvec_write_field(dest, wp,0x0,1); // spare + bitvec_write_field(dest, wp,0x0,1); // TMA : Two-message assignment: No meaning + bitvec_write_field(dest, wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode + bitvec_write_field(dest, wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF). + + bitvec_write_field(dest, wp,0x0,4); // Page Mode + + // GSM 04.08 10.5.2.25a Packet Channel Description + bitvec_write_field(dest, wp,0x1,5); // Channel type + bitvec_write_field(dest, wp,ts,3); // TN + bitvec_write_field(dest, wp,tsc,3); // TSC + bitvec_write_field(dest, wp,0x0,3); // non-hopping RF channel configuraion + bitvec_write_field(dest, wp,arfcn,10); // ARFCN + + //10.5.2.30 Request Reference + bitvec_write_field(dest, wp,ra,8); // RA + bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1' + bitvec_write_field(dest, wp,ref_fn % 51,6); // T3 + bitvec_write_field(dest, wp,ref_fn % 26,5); // T2 + + // 10.5.2.40 Timing Advance + bitvec_write_field(dest, wp,0x0,2); // spare + bitvec_write_field(dest, wp,ta,6); // Timing Advance value + + // No mobile allocation in non-hopping systems. + // A zero-length LV. Just write L=0. + bitvec_write_field(dest, wp,0,8); + + if ((wp % 8)) { + LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS without rest " + "octets is not multiple of 8 bits, PLEASE FIX!\n"); + exit (0); + } + plen = wp / 8; + + if (downlink) + { + // GSM 04.08 10.5.2.16 IA Rest Octets + bitvec_write_field(dest, wp, 3, 2); // "HH" + bitvec_write_field(dest, wp, 1, 2); // "01" Packet Downlink Assignment + bitvec_write_field(dest, wp,tlli,32); // TLLI + bitvec_write_field(dest, wp,0x1,1); // switch TFI : on + bitvec_write_field(dest, wp,tfi,5); // TFI + bitvec_write_field(dest, wp,0x0,1); // RLC acknowledged mode + if (alpha) { + bitvec_write_field(dest, wp,0x1,1); // ALPHA = present + bitvec_write_field(dest, wp,alpha,4); // ALPHA + } else { + bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present + } + bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter + bitvec_write_field(dest, wp,polling,1); // Polling Bit + bitvec_write_field(dest, wp,!polling,1); // TA_VALID ??? + if (ta_idx < 0) { + bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off + } else { + bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on + bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX + } + if (polling) { + bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present + bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1' + bitvec_write_field(dest, wp,fn % 51,6); // T3 + bitvec_write_field(dest, wp,fn % 26,5); // T2 + } else { + bitvec_write_field(dest, wp,0x0,1); // TBF Starting TIME present + } + bitvec_write_field(dest, wp,0x0,1); // P0 not present +// bitvec_write_field(dest, wp,0x1,1); // P0 not present +// bitvec_write_field(dest, wp,0xb,4); + } + else + { + // GMS 04.08 10.5.2.37b 10.5.2.16 + bitvec_write_field(dest, wp, 3, 2); // "HH" + bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment + if (single_block) { + bitvec_write_field(dest, wp, 0, 1); // Block Allocation : Single Block Allocation + if (alpha) { + bitvec_write_field(dest, wp,0x1,1); // ALPHA = present + bitvec_write_field(dest, wp,alpha,4); // ALPHA = present + } else + bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present + bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter + if (ta_idx < 0) { + bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off + } else { + bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on + bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX + } + bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG + bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1' + bitvec_write_field(dest, wp,fn % 51,6); // T3 + bitvec_write_field(dest, wp,fn % 26,5); // T2 + } else { + bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation + bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity + bitvec_write_field(dest, wp, 0, 1); // POLLING + bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic + bitvec_write_field(dest, wp, usf, 3); // USF + bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY + bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present + bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND + bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING + if (alpha) { + bitvec_write_field(dest, wp,0x1,1); // ALPHA = present + bitvec_write_field(dest, wp,alpha,4); // ALPHA + } else + bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present + bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter + /* note: there is no choise for TAI and no starting time */ + bitvec_write_field(dest, wp, 0, 1); // switch TIMING_ADVANCE_INDEX = off + bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG + } + } + + return plen; +} + +/* generate uplink assignment */ +void Encoding::write_packet_uplink_assignment( + struct gprs_rlcmac_bts *bts, + bitvec * dest, uint8_t old_tfi, + uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, + struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha, + uint8_t gamma, int8_t ta_idx) +{ + // TODO We should use our implementation of encode RLC/MAC Control messages. + unsigned wp = 0; + uint8_t ts; + + bitvec_write_field(dest, wp,0x1,2); // Payload Type + bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13) + bitvec_write_field(dest, wp,poll,1); // Suppl/Polling Bit + bitvec_write_field(dest, wp,0x0,3); // Uplink state flag + bitvec_write_field(dest, wp,0xa,6); // MESSAGE TYPE + + bitvec_write_field(dest, wp,0x0,2); // Page Mode + + bitvec_write_field(dest, wp,0x0,1); // switch PERSIST_LEVEL: off + if (use_tlli) { + bitvec_write_field(dest, wp,0x2,2); // switch TLLI : on + bitvec_write_field(dest, wp,tlli,32); // TLLI + } else { + bitvec_write_field(dest, wp,0x0,1); // switch TFI : on + bitvec_write_field(dest, wp,old_downlink,1); // 0=UPLINK TFI, 1=DL TFI + bitvec_write_field(dest, wp,old_tfi,5); // TFI + } + + bitvec_write_field(dest, wp,0x0,1); // Message escape + bitvec_write_field(dest, wp,bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND + bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING + bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on + bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE + if (ta_idx < 0) { + bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off + } else { + bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on + bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX + } + +#if 1 + bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present + bitvec_write_field(dest, wp,tbf->tsc(),3); // Training Sequence Code (TSC) + bitvec_write_field(dest, wp,0x0,2); // ARFCN = present + bitvec_write_field(dest, wp,tbf->trx->arfcn,10); // ARFCN +#else + bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off +#endif + + bitvec_write_field(dest, wp,0x1,2); // Dynamic Allocation + + bitvec_write_field(dest, wp,0x0,1); // Extended Dynamic Allocation = off + bitvec_write_field(dest, wp,0x0,1); // P0 = off + + bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY + bitvec_write_field(dest, wp,0x1,1); // switch TFI : on + bitvec_write_field(dest, wp,tbf->tfi(),5);// TFI + + bitvec_write_field(dest, wp,0x0,1); // + bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off + if (alpha || gamma) { + bitvec_write_field(dest, wp,0x1,1); // Timeslot Allocation with Power Control + bitvec_write_field(dest, wp,alpha,4); // ALPHA + } else + bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation + + for (ts = 0; ts < 8; ts++) { + if (tbf->pdch[ts]) { + bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on + bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i) + if (alpha || gamma) + bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter + } else + bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off + } +// bitvec_write_field(dest, wp,0x0,1); // Measurement Mapping struct not present +} + + +/* generate downlink assignment */ +void Encoding::write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi, + uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll, + uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts) +{ + // Packet downlink assignment TS 44.060 11.2.7 + + uint8_t tn; + + block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header + block->RRBP = 0x0; // N+13 + block->SP = poll; // RRBP field is valid + block->USF = 0x0; // Uplink state flag + + block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2; // Packet Downlink Assignment + block->u.Packet_Downlink_Assignment.PAGE_MODE = 0x0; // Normal Paging + + block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL = 0x0; // PERSISTENCE_LEVEL: off + + block->u.Packet_Downlink_Assignment.ID.UnionType = 0x0; // TFI = on + block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType = old_downlink; // 0=UPLINK TFI, 1=DL TFI + block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi; // TFI + + block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation + block->u.Packet_Downlink_Assignment.RLC_MODE = 0x0; // RLC acknowledged mode + block->u.Packet_Downlink_Assignment.CONTROL_ACK = old_downlink; // NW establishes no new DL TBF for the MS with running timer T3192 + block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s) + for (tn = 0; tn < 8; tn++) { + if (tbf->pdch[tn]) + block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s) + } + + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta; // TIMING_ADVANCE_VALUE + if (ta_idx < 0) { + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off + } else { + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x1; // TIMING_ADVANCE_INDEX = on + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_INDEX = ta_idx; // TIMING_ADVANCE_INDEX + block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_TIMESLOT_NUMBER = ta_ts; // TIMING_ADVANCE_TS + } + + block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off + + block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on + block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tbf->tsc(); // Training Sequence Code (TSC) + block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0; // ARFCN = on + block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = tbf->trx->arfcn; // ARFCN + + block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on + block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi(); // TFI + + block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on + block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA + + for (tn = 0; tn < 8; tn++) + { + if (tbf->pdch[tn]) + { + block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on + block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN + } + else + { + block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off + } + } + + block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time = 0x0; // TBF Starting TIME = off + block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off + block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off +} + +/* generate paging request */ +int Encoding::write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len) +{ + unsigned wp = 0; + int plen; + + bitvec_write_field(dest, wp,0x0,4); // Skip Indicator + bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator + bitvec_write_field(dest, wp,0x21,8); // Paging Request Message Type + + bitvec_write_field(dest, wp,0x0,4); // Page Mode + bitvec_write_field(dest, wp,0x0,4); // Channel Needed + + // Mobile Identity + bitvec_write_field(dest, wp,ptmsi_len+1,8); // Mobile Identity length + bitvec_write_field(dest, wp,0xf,4); // unused + bitvec_write_field(dest, wp,0x4,4); // PTMSI type + for (int i = 0; i < ptmsi_len; i++) + { + bitvec_write_field(dest, wp,ptmsi[i],8); // PTMSI + } + if ((wp % 8)) { + LOGP(DRLCMACUL, LOGL_ERROR, "Length of PAG.REQ without rest " + "octets is not multiple of 8 bits, PLEASE FIX!\n"); + exit (0); + } + plen = wp / 8; + bitvec_write_field(dest, wp,0x0,1); // "L" NLN(PCH) = off + bitvec_write_field(dest, wp,0x0,1); // "L" Priority1 = off + bitvec_write_field(dest, wp,0x1,1); // "L" Priority2 = off + bitvec_write_field(dest, wp,0x0,1); // "L" Group Call information = off + bitvec_write_field(dest, wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure + bitvec_write_field(dest, wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure + + return plen; +} + +/** + * The index of the array show_rbb is the bit position inside the rbb + * (show_rbb[63] relates to BSN ssn-1) + */ +void Encoding::encode_rbb(const char *show_rbb, uint8_t *rbb) +{ + uint8_t rbb_byte = 0; + + // RECEIVE_BLOCK_BITMAP + for (int i = 0; i < 64; i++) { + /* Set bit at the appropriate position (see 3GPP TS 04.60 9.1.8.1) */ + if (show_rbb[i] == 'R') + rbb_byte |= 1<< (7-(i%8)); + + if((i%8) == 7) { + rbb[i/8] = rbb_byte; + rbb_byte = 0; + } + } +} + +/* generate uplink ack */ +void Encoding::write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, + RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf, + uint8_t final) +{ + // Packet Uplink Ack/Nack TS 44.060 11.2.28 + + char rbb[65]; + + tbf->dir.ul.window.update_rbb(rbb); + + LOGP(DRLCMACUL, LOGL_DEBUG, "Encoding Ack/Nack for %s " + "(final=%d)\n", tbf_name(tbf), final); + + block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header + block->RRBP = 0x0; // N+13 + block->SP = final; // RRBP field is valid, if it is final ack + block->USF = 0x0; // Uplink state flag + + block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9; // Packet Downlink Assignment + block->u.Packet_Uplink_Ack_Nack.PAGE_MODE = 0x0; // Normal Paging + block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi(); // Uplink TFI + + block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = bts->initial_cs_ul - 1; // CS1 + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.window.ssn(); // STARTING_SEQUENCE_NUMBER + + encode_rbb(rbb, block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP); + + /* rbb is not NULL terminated */ + rbb[64] = 0; + LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received " + "I=Invalid\n", rbb); + + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType = 0x0; // Fixed Allocation Dummy = on + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.u.FixedAllocationDummy = 0x0; // Fixed Allocation Dummy + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off + + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_CONTENTION_RESOLUTION_TLLI = 0x1; + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI = tbf->tlli(); + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Packet_Timing_Advance = 0x0; + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Extension_Bits = 0x0; + block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0; +} + +unsigned Encoding::write_packet_paging_request(bitvec * dest) +{ + unsigned wp = 0; + + bitvec_write_field(dest, wp,0x1,2); // Payload Type + bitvec_write_field(dest, wp,0x0,3); // No polling + bitvec_write_field(dest, wp,0x0,3); // Uplink state flag + bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE + + bitvec_write_field(dest, wp,0x0,2); // Page Mode + + bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL + bitvec_write_field(dest, wp,0x0,1); // No NLN + + return wp; +} + +unsigned Encoding::write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len, + uint8_t *identity, uint8_t chan_needed) +{ + bitvec_write_field(dest, wp,0x1,1); // Repeated Page info exists + + bitvec_write_field(dest, wp,0x1,1); // RR connection paging + + if ((identity[0] & 0x07) == 4) { + bitvec_write_field(dest, wp,0x0,1); // TMSI + identity++; + len--; + } else { + bitvec_write_field(dest, wp,0x0,1); // MI + bitvec_write_field(dest, wp,len,4); // MI len + } + while (len) { + bitvec_write_field(dest, wp,*identity++,8); // MI data + len--; + } + bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED + bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY + + return wp; +} + diff --git a/src/encoding.h b/src/encoding.h new file mode 100644 index 00000000..e62c2c8f --- /dev/null +++ b/src/encoding.h @@ -0,0 +1,68 @@ +/* encoding.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#pragma once + +#include <stdint.h> +#include <gsm_rlcmac.h> + +struct gprs_rlcmac_bts; +struct gprs_rlcmac_tbf; +struct bitvec; + +/** + * I help with encoding data into CSN1 messages. + * TODO: Nobody can remember a function signature like this. One should + * fill out a struct with the request parameters and then hand the struct + * to the code. + */ +class Encoding { +public: + static int write_immediate_assignment( + struct gprs_rlcmac_bts *bts, + bitvec * dest, uint8_t downlink, uint8_t ra, + uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, + uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling, + uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma, + int8_t ta_idx); + + static void write_packet_uplink_assignment( + struct gprs_rlcmac_bts *bts, + bitvec * dest, uint8_t old_tfi, + uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, + struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha, + uint8_t gamma, int8_t ta_idx); + + static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi, + uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll, + uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts); + + static void encode_rbb(const char *show_rbb, uint8_t *rbb); + + static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf, + uint8_t final); + + static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len); + + static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len, + uint8_t *identity, uint8_t chan_needed); + + static unsigned write_packet_paging_request(bitvec * dest); +}; diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index 3432bca8..133eb972 100644 --- a/src/gprs_bssgp_pcu.cpp +++ b/src/gprs_bssgp_pcu.cpp @@ -21,6 +21,7 @@ #include <gprs_rlcmac.h> #include <gprs_bssgp_pcu.h> #include <pcu_l1_if.h> +#include <bts.h> #include <tbf.h> static struct gprs_bssgp_pcu the_pcu = { 0, }; @@ -93,7 +94,6 @@ static int parse_ra_cap_ms_class(struct tlv_parsed *tp) } if (bitvec_read_field(block, rp, 1)) // SMS Present bitvec_read_field(block, rp, 4); // SMS Value - bitvec_read_field(block, rp, 4); // SMS Value } bitvec_free(block); @@ -121,7 +121,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU); len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); - if (len > sizeof(gprs_rlcmac_tbf::llc_frame)) + if (len > sizeof(gprs_llc::frame)) { LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli); return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg); @@ -152,7 +152,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len); - return tbf_handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len); + return gprs_rlcmac_tbf::handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len); } int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp) diff --git a/src/gprs_bssgp_pcu.h b/src/gprs_bssgp_pcu.h index 7156bf08..aedcfc00 100644 --- a/src/gprs_bssgp_pcu.h +++ b/src/gprs_bssgp_pcu.h @@ -42,6 +42,8 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei); #define NS_HDR_LEN 4 #define IE_LLC_PDU 14 +struct gprs_rlcmac_bts; + struct gprs_bssgp_pcu { struct gprs_nsvc *nsvc; struct bssgp_bvc_ctx *bctx; diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp index 9b4e6436..ac8f2d12 100644 --- a/src/gprs_rlcmac.cpp +++ b/src/gprs_rlcmac.cpp @@ -22,1451 +22,20 @@ #include <gprs_bssgp_pcu.h> #include <pcu_l1_if.h> #include <gprs_rlcmac.h> +#include <bts.h> +#include <encoding.h> #include <tbf.h> -/* 3GPP TS 05.02 Annex B.1 */ -#define MS_NA 255 /* N/A */ -#define MS_A 254 /* 1 with hopping, 0 without */ -#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/ -#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/ - -struct gprs_ms_multislot_class { - uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */ - uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */ - uint8_t type; /* Type of Mobile */ -}; - -static const struct gprs_ms_multislot_class gprs_ms_multislot_class[32] = { -/* M-S Class Rx Tx Sum Tta Ttb Tra Trb Type */ -/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, -/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 }, -/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 }, -/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 }, -/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 }, -/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 }, -/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 }, -/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 }, -/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 }, -/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 }, -/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 }, -/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 }, -/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 }, -/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, -/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, -/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, -/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 }, -/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 }, -/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 }, -/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 }, -/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 }, -/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 }, -/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 }, -/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 }, -/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 }, -/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, -/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, -}; - -struct gprs_rlcmac_cs gprs_rlcmac_cs[] = { -/* frame length data block max payload */ - { 0, 0, 0 }, - { 23, 23, 20 }, /* CS-1 */ - { 34, 33, 30 }, /* CS-2 */ - { 40, 39, 36 }, /* CS-3 */ - { 54, 53, 50 }, /* CS-4 */ -}; - -LLIST_HEAD(gprs_rlcmac_ul_tbfs); -LLIST_HEAD(gprs_rlcmac_dl_tbfs); -llist_head *gprs_rlcmac_tbfs_lists[] = { - &gprs_rlcmac_ul_tbfs, - &gprs_rlcmac_dl_tbfs, - NULL -}; extern void *tall_pcu_ctx; -#ifdef DEBUG_DIAGRAM -struct timeval diagram_time = {0,0}; -struct timeval diagram_last_tv = {0,0}; - -void debug_diagram(int diag, const char *format, ...) -{ - va_list ap; - char debug[128]; - char line[1024]; - struct gprs_rlcmac_tbf *tbf, *tbf_a[16]; - int max_diag = -1, i; - uint64_t diff = 0; - - va_start(ap, format); - vsnprintf(debug, sizeof(debug) - 1, format, ap); - debug[19] = ' '; - debug[20] = '\0'; - va_end(ap); - - memset(tbf_a, 0, sizeof(tbf_a)); - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { - if (tbf->diag < 16) { - if (tbf->diag > max_diag) - max_diag = tbf->diag; - tbf_a[tbf->diag] = tbf; - } - } - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { - if (tbf->diag < 16) { - if (tbf->diag > max_diag) - max_diag = tbf->diag; - tbf_a[tbf->diag] = tbf; - } - } - - if (diagram_last_tv.tv_sec) { - diff = (uint64_t)(diagram_time.tv_sec - - diagram_last_tv.tv_sec) * 1000; - diff += diagram_time.tv_usec / 1000; - diff -= diagram_last_tv.tv_usec / 1000; - } - memcpy(&diagram_last_tv, &diagram_time, sizeof(struct timeval)); - - if (diff > 0) { - if (diff > 99999) - strcpy(line, " ... : "); - else - sprintf(line, "%3d.%03d: ", (int)(diff / 1000), - (int)(diff % 1000)); - for (i = 0; i <= max_diag; i++) { - if (tbf_a[i] == NULL) { - strcat(line, " "); - continue; - } - if (tbf_a[i]->diag_new) { - strcat(line, " | "); - continue; - } - strcat(line, " "); - } - puts(line); - } - strcpy(line, " : "); - for (i = 0; i <= max_diag; i++) { - if (tbf_a[i] == NULL) { - strcat(line, " "); - continue; - } - if (tbf_a[i]->diag != diag) { - strcat(line, " | "); - continue; - } - if (strlen(debug) < 19) { - strcat(line, " "); - memcpy(line + strlen(line) - 11 - strlen(debug) / 2, - debug, strlen(debug)); - } else - strcat(line, debug); - tbf_a[i]->diag_new = 1; - } - puts(line); -} -#endif - -/* FIXME: spread resources over multiple TRX. Also add option to use same - * TRX in case of existing TBF for TLLI in the other direction. */ -/* search for free TFI and return TFI, TRX */ -int tfi_find_free(struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir, - uint8_t *_trx, int8_t use_trx) -{ - struct gprs_rlcmac_pdch *pdch; - struct gprs_rlcmac_tbf **tbfp; - uint8_t trx_from, trx_to, trx, ts, tfi; - - if (use_trx >= 0 && use_trx < 8) - trx_from = trx_to = use_trx; - else { - trx_from = 0; - trx_to = 7; - } - - /* on TRX find first enabled TS */ - for (trx = trx_from; trx <= trx_to; trx++) { - for (ts = 0; ts < 8; ts++) { - pdch = &bts->trx[trx].pdch[ts]; - if (!pdch->enable) - continue; - break; - } - if (ts < 8) - break; - } - if (trx > trx_to) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n"); - return -EINVAL; - } - - - LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: " - "TRX=%d first TS=%d\n", trx, ts); - if (dir == GPRS_RLCMAC_UL_TBF) - tbfp = bts->trx[trx].ul_tbf; - else - tbfp = bts->trx[trx].dl_tbf; - for (tfi = 0; tfi < 32; tfi++) { - if (!tbfp[tfi]) - break; - } - - if (tfi < 32) { - LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi); - *_trx = trx; - return tfi; - } - LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n"); - - return -1; -} - -static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch, uint8_t ts) -{ - struct gprs_rlcmac_tbf *tbf; - uint8_t usf_map = 0; - uint8_t tfi, usf; - - /* make map of used USF */ - for (tfi = 0; tfi < 32; tfi++) { - tbf = pdch->ul_tbf[tfi]; - if (!tbf) - continue; - usf_map |= (1 << tbf->dir.ul.usf[ts]); - } - - /* look for USF, don't use USF=7 */ - for (usf = 0; usf < 7; usf++) { - if (!(usf_map & (1 << usf))) - return usf; - } - - return -1; -} - -/* Slot Allocation: Algorithm A - * - * Assign single slot for uplink and downlink - */ -int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, - struct gprs_rlcmac_tbf *old_tbf, - struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single) -{ - struct gprs_rlcmac_pdch *pdch; - uint8_t ts; - int8_t usf; /* must be signed */ - - LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class " - "%d\n", tbf->ms_class); - - for (ts = 0; ts < 8; ts++) { - pdch = &bts->trx[tbf->trx].pdch[ts]; - if (!pdch->enable) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because " - "not enabled\n", ts); - continue; - } - break; - } - if (ts == 8) - return -EINVAL; - - tbf->tsc = pdch->tsc; - if (tbf->direction == GPRS_RLCMAC_UL_TBF) { - /* if USF available */ - usf = find_free_usf(pdch, ts); - if (usf >= 0) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink " - "TS=%d USF=%d\n", ts, usf); - bts->trx[tbf->trx].ul_tbf[tbf->tfi] = tbf; - pdch->ul_tbf[tbf->tfi] = tbf; - tbf->pdch[ts] = pdch; - } else { - LOGP(DRLCMAC, LOGL_NOTICE, "- Failed " - "allocating TS=%d, no USF available\n", ts); - return -EBUSY; - } - } else { - LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d\n", ts); - bts->trx[tbf->trx].dl_tbf[tbf->tfi] = tbf; - pdch->dl_tbf[tbf->tfi] = tbf; - tbf->pdch[ts] = pdch; - } - /* the only one TS is the common TS */ - tbf->first_ts = tbf->first_common_ts = ts; - - return 0; -} - -/* Slot Allocation: Algorithm B - * - * Assign as many downlink slots as possible. - * Assign one uplink slot. (With free USF) - * - */ -int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, - struct gprs_rlcmac_tbf *old_tbf, - struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single) -{ - struct gprs_rlcmac_pdch *pdch; - const struct gprs_ms_multislot_class *ms_class; - uint8_t Rx, Tx, Sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */ - uint8_t Tta, Ttb, Tra, Trb, Tt, Tr; /* Minimum Number of Slots */ - uint8_t Type; /* Type of Mobile */ - uint8_t rx_win_min = 0, rx_win_max = 7; - uint8_t tx_win_min, tx_win_max, tx_range; - uint8_t rx_window = 0, tx_window = 0; - static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" }; - int8_t usf[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; /* must be signed */ - int8_t tsc = -1; /* must be signed */ - int8_t first_common_ts = -1; - uint8_t i, ts; - uint8_t slotcount = 0; - - - if (tbf->ms_class >= 32) { - LOGP(DRLCMAC, LOGL_ERROR, "Multislot class %d out of range.\n", - tbf->ms_class); - return -EINVAL; - } - - if (tbf->ms_class) { - ms_class = &gprs_ms_multislot_class[tbf->ms_class]; - LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for " - "class %d\n", tbf->ms_class); - } else { - ms_class = &gprs_ms_multislot_class[12]; - LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for " - "unknow class (assuming 12)\n"); - } - - if (ms_class->tx == MS_NA) { - LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not " - "applicable.\n", tbf->ms_class); - return -EINVAL; - } - - Rx = ms_class->rx; -#if 0 - if (Rx > 4) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Degrading max Rx slots to 4\n"); - Rx = 4; - } -#endif - Tx = ms_class->tx; -#if 0 - if (Tx > 4) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Degrading max Tx slots to 4\n"); - Tx = 4; - } -#endif - Sum = ms_class->sum; - Tta = ms_class->ta; - Ttb = ms_class->tb; - Tra = ms_class->ra; - Trb = ms_class->rb; - Type = ms_class->type; - - /* Tta and Ttb may depend on hopping or frequency change */ - if (Ttb == MS_A) { - if (/* FIXME: hopping*/ 0) - Ttb = 1; - else - Ttb = 0; - } - if (Trb == MS_A) { - if (/* FIXME: hopping*/ 0) - Trb = 1; - else - Trb = 0; - } - if (Ttb == MS_B) { - /* FIXME: or frequency change */ - if (/* FIXME: hopping*/ 0) - Ttb = 1; - else - Ttb = 0; - } - if (Trb == MS_C) { - /* FIXME: or frequency change */ - if (/* FIXME: hopping*/ 0) - Trb = 1; - else - Trb = 0; - } - - LOGP(DRLCMAC, LOGL_DEBUG, "- Rx=%d Tx=%d Sum Rx+Tx=%s Tta=%s Ttb=%d " - " Tra=%d Trb=%d Type=%d\n", Rx, Tx, - (Sum == MS_NA) ? "N/A" : digit[Sum], - (Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type); - - /* select the values for time contraints */ - if (/* FIXME: monitoring */0) { - /* applicable to type 1 and type 2 */ - Tt = Ttb; - Tr = Tra; - } else { - /* applicable to type 1 and type 2 */ - Tt = Ttb; - Tr = Trb; - } - - /* select a window of Rx slots if available - * The maximum allowed slots depend on RX or the window of available - * slots. - * This must be done for uplink TBF also, because it is the basis - * for calculating control slot and uplink slot(s). */ - for (ts = 0, i = 0; ts < 8; ts++) { - pdch = &bts->trx[tbf->trx].pdch[ts]; - /* check if enabled */ - if (!pdch->enable) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because " - "not enabled\n", ts); - /* increase window for Type 1 */ - if (Type == 1 && rx_window) - i++; - continue; - } - /* check if TSC changes */ - if (tsc < 0) - tbf->tsc = tsc = pdch->tsc; - else if (tsc != pdch->tsc) { - LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of TRX=%d, " - "because it has different TSC than lower TS " - "of TRX. In order to allow multislot, all " - "slots must be configured with the same " - "TSC!\n", ts, tbf->trx); - /* increase window for Type 1 */ - if (Type == 1 && rx_window) - i++; - continue; - } - - if (!rx_window) - rx_win_min = ts; - - rx_window |= (1 << ts); - LOGP(DRLCMAC, LOGL_DEBUG, "- Selected DL TS %d\n", ts); - - /* range of window (required for Type 1) */ - rx_win_max = ts; - - if (++i == Rx) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / " - "window reached maximum alowed Rx size\n"); - break; - } - } - - LOGP(DRLCMAC, LOGL_DEBUG, "- Selected slots for RX: " - "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", - ((rx_window & 0x01)) ? 'D' : '.', - ((rx_window & 0x02)) ? 'D' : '.', - ((rx_window & 0x04)) ? 'D' : '.', - ((rx_window & 0x08)) ? 'D' : '.', - ((rx_window & 0x10)) ? 'D' : '.', - ((rx_window & 0x20)) ? 'D' : '.', - ((rx_window & 0x40)) ? 'D' : '.', - ((rx_window & 0x80)) ? 'D' : '.'); - - /* reduce window, if existing uplink slots collide RX window */ - if (Type == 1 && old_tbf && old_tbf->direction == GPRS_RLCMAC_UL_TBF) { - uint8_t collide = 0, ul_usage = 0; - int j; - - /* calculate mask of colliding slots */ - for (ts = 0; ts < 8; ts++) { - if (old_tbf->pdch[ts]) { - ul_usage |= (1 << ts); - /* mark bits from TS-t .. TS+r */ - for (j = ts - Tt; j != ((ts + Tr + 1) & 7); - j = (j + 1) & 7) - collide |= (1 << j); - } - } - LOGP(DRLCMAC, LOGL_DEBUG, "- Not allowed slots due to existing " - "UL allocation: (TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7) " - " D=downlink x=not usable\n", - ((ul_usage & 0x01)) ? 'D' : ((collide & 0x01))?'x':'.', - ((ul_usage & 0x02)) ? 'D' : ((collide & 0x02))?'x':'.', - ((ul_usage & 0x04)) ? 'D' : ((collide & 0x04))?'x':'.', - ((ul_usage & 0x08)) ? 'D' : ((collide & 0x08))?'x':'.', - ((ul_usage & 0x10)) ? 'D' : ((collide & 0x10))?'x':'.', - ((ul_usage & 0x20)) ? 'D' : ((collide & 0x20))?'x':'.', - ((ul_usage & 0x40)) ? 'D' : ((collide & 0x40))?'x':'.', - ((ul_usage & 0x80)) ? 'D' : ((collide & 0x80))?'x':'.'); - - /* apply mask to reduce tx_window (shifted by 3 slots) */ - rx_window &= ~(collide << 3); - rx_window &= ~(collide >> 5); - LOGP(DRLCMAC, LOGL_DEBUG, "- Remaining slots for RX: " - "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", - ((rx_window & 0x01)) ? 'D' : '.', - ((rx_window & 0x02)) ? 'D' : '.', - ((rx_window & 0x04)) ? 'D' : '.', - ((rx_window & 0x08)) ? 'D' : '.', - ((rx_window & 0x10)) ? 'D' : '.', - ((rx_window & 0x20)) ? 'D' : '.', - ((rx_window & 0x40)) ? 'D' : '.', - ((rx_window & 0x80)) ? 'D' : '.'); - if (!rx_window) { - LOGP(DRLCMAC, LOGL_NOTICE, "No suitable downlink slots " - "available with current uplink assignment\n"); - return -EBUSY; - } - - /* calculate new min/max */ - for (ts = rx_win_min; ts <= rx_win_max; ts++) { - if ((rx_window & (1 << ts))) - break; - rx_win_min = ts + 1; - LOGP(DRLCMAC, LOGL_DEBUG, "- TS has been deleted, so " - "raising start of DL window to %d\n", - rx_win_min); - } - for (ts = rx_win_max; ts >= rx_win_min; ts--) { - if ((rx_window & (1 << ts))) - break; - rx_win_max = ts - 1; - LOGP(DRLCMAC, LOGL_DEBUG, "- TS has been deleted, so " - "lowering end of DL window to %d\n", - rx_win_max); - } - } - - /* reduce window, to allow at least one uplink TX slot - * this is only required for Type 1 */ - if (Type == 1 && rx_win_max - rx_win_min + 1 + Tt + 1 + Tr > 8) { - rx_win_max = rx_win_min + 7 - Tt - 1 - Tr; - LOGP(DRLCMAC, LOGL_DEBUG, "- Reduce RX window due to time " - "contraints to %d slots\n", - rx_win_max - rx_win_min + 1); - } - - LOGP(DRLCMAC, LOGL_DEBUG, "- RX-Window is: %d..%d\n", rx_win_min, - rx_win_max); - - /* calculate TX window */ - if (Type == 1) { - /* calculate TX window (shifted by 3 timeslots) - * it uses the space between tx_win_max and tx_win_min */ - tx_win_min = (rx_win_max - 2 + Tt) & 7; - tx_win_max = (rx_win_min + 4 - Tr) & 7; - /* calculate the TX window size (might be larger than Tx) */ - tx_range = (tx_win_max - tx_win_min + 1) & 7; - } else { - /* TX and RX simultaniously */ - tx_win_min = rx_win_min; - tx_win_max = 7; - /* TX window size (might be larger than Tx) */ - tx_range = tx_win_max - tx_win_min + 1; - } - - LOGP(DRLCMAC, LOGL_DEBUG, "- TX-Window is: %d..%d\n", tx_win_min, - tx_win_max); - - /* select a window of Tx slots if available - * The maximum allowed slots depend on TX or the window of available - * slots. - * - * also assign the first common ts, which is used for control or single - * slot. */ - if (tbf->direction == GPRS_RLCMAC_UL_TBF) { - for (ts = tx_win_min, i = 0; i < tx_range; ts = (ts + 1) & 7) { - pdch = &bts->trx[tbf->trx].pdch[ts]; - /* check if enabled */ - if (!pdch->enable) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " - "because not enabled\n", ts); - continue; - } - /* check if TSC changes */ - if (tsc < 0) - tbf->tsc = tsc = pdch->tsc; - else if (tsc != pdch->tsc) { - LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of " - "TRX=%d, because it has different TSC " - "than lower TS of TRX. In order to " - "allow multislot, all slots must be " - "configured with the same TSC!\n", - ts, tbf->trx); - /* increase window for Type 1 */ - if (Type == 1) - i++; - continue; - } - /* check for free usf */ - usf[ts] = find_free_usf(pdch, ts); - if (usf[ts] < 0) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " - "because no USF available\n", ts); - /* increase window for Type 1 */ - if (Type == 1) - i++; - continue; - } - - if (!tx_window) - first_common_ts = ts; - - tx_window |= (1 << ts); - LOGP(DRLCMAC, LOGL_DEBUG, "- Selected UL TS %d\n", ts); - - if (1 && Type == 1) { /* FIXME: multislot UL assignment */ - LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because " - "1 slot assigned\n"); - break; - } - if (++i == Tx) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because " - "slots / window reached maximum " - "allowed Tx size\n"); - break; - } - } - - LOGP(DRLCMAC, LOGL_DEBUG, "- Selected TX window: " - "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", - ((tx_window & 0x01)) ? 'U' : '.', - ((tx_window & 0x02)) ? 'U' : '.', - ((tx_window & 0x04)) ? 'U' : '.', - ((tx_window & 0x08)) ? 'U' : '.', - ((tx_window & 0x10)) ? 'U' : '.', - ((tx_window & 0x20)) ? 'U' : '.', - ((tx_window & 0x40)) ? 'U' : '.', - ((tx_window & 0x80)) ? 'U' : '.'); - - if (!tx_window) { - LOGP(DRLCMAC, LOGL_NOTICE, "No suitable uplink slots " - "available\n"); - return -EBUSY; - } - } else { - /* assign the first common ts, which is used for control or - * single slot. */ - for (ts = tx_win_min, i = 0; i < tx_range; ts = (ts + 1) & 7) { - pdch = &bts->trx[tbf->trx].pdch[ts]; - /* check if enabled */ - if (!pdch->enable) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " - "because not enabled\n", ts); - continue; - } - first_common_ts = ts; - break; - } - } - - if (first_common_ts < 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n"); - return -EINVAL; - } - - if (tbf->direction == GPRS_RLCMAC_DL_TBF) { - /* assign downlink */ - if (rx_window == 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No downlink slots " - "available\n"); - return -EINVAL; - } - for (ts = 0; ts < 8; ts++) { - if ((rx_window & (1 << ts))) { - /* be sure to select a single downlink slots - * that can be used for uplink, if multiple - * slots are assigned later. */ - if (single && first_common_ts != ts) - continue; - LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS " - "%d\n", ts); - pdch = &bts->trx[tbf->trx].pdch[ts]; - bts->trx[tbf->trx].dl_tbf[tbf->tfi] = tbf; - pdch->dl_tbf[tbf->tfi] = tbf; - tbf->pdch[ts] = pdch; - slotcount++; - if (slotcount == 1) - tbf->first_ts = ts; - if (single) - break; - } - } - } else { - /* assign uplink */ - if (tx_window == 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No uplink slots " - "available\n"); - return -EINVAL; - } - for (ts = 0; ts < 8; ts++) { - if ((tx_window & (1 << ts))) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS " - "%d\n", ts); - pdch = &bts->trx[tbf->trx].pdch[ts]; - bts->trx[tbf->trx].ul_tbf[tbf->tfi] = tbf; - pdch->ul_tbf[tbf->tfi] = tbf; - tbf->pdch[ts] = pdch; - tbf->dir.ul.usf[ts] = usf[ts]; - slotcount++; - if (slotcount == 1) - tbf->first_ts = ts; - if (single) - break; - } - } - } - if (single && slotcount) { - LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n", - tbf->first_ts, - (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL"); - } else { - LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount, - (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL"); - } - if (slotcount == 0) - return -EBUSY; - - tbf->first_common_ts = first_common_ts; - - return 0; -} - -/* starting time for assigning single slot - * This offset must be a multiple of 13. */ -#define AGCH_START_OFFSET 52 - -LLIST_HEAD(gprs_rlcmac_sbas); - -int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta) -{ - - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct gprs_rlcmac_pdch *pdch; - struct gprs_rlcmac_sba *sba; - uint8_t trx, ts; - uint32_t fn; - - sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba); - if (!sba) - return -ENOMEM; - - for (trx = 0; trx < 8; trx++) { - for (ts = 0; ts < 8; ts++) { - pdch = &bts->trx[trx].pdch[ts]; - if (!pdch->enable) - continue; - break; - } - if (ts < 8) - break; - } - if (trx == 8) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n"); - talloc_free(sba); - return -EINVAL; - } - - fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648; - - sba->trx = trx; - sba->ts = ts; - sba->fn = fn; - sba->ta = ta; - - llist_add(&sba->list, &gprs_rlcmac_sbas); - - *_trx = trx; - *_ts = ts; - *_fn = fn; - return 0; -} - -struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn) -{ - struct gprs_rlcmac_sba *sba; - - llist_for_each_entry(sba, &gprs_rlcmac_sbas, list) { - if (sba->trx == trx && sba->ts == ts && sba->fn == fn) - return sba; - } - - return NULL; -} - -/* received RLC/MAC block from L1 */ -int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len, - uint32_t fn, int8_t rssi) -{ - unsigned payload = data[0] >> 6; - bitvec *block; - int rc = 0; - - switch (payload) { - case GPRS_RLCMAC_DATA_BLOCK: - rc = gprs_rlcmac_rcv_data_block_acknowledged(trx, ts, data, - len, rssi); - break; - case GPRS_RLCMAC_CONTROL_BLOCK: - block = bitvec_alloc(len); - if (!block) - return -ENOMEM; - bitvec_unpack(block, data); - rc = gprs_rlcmac_rcv_control_block(block, trx, ts, fn); - bitvec_free(block); - break; - case GPRS_RLCMAC_CONTROL_BLOCK_OPT: - LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n"); - break; - default: - LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload); - rc = -EINVAL; - } - - return rc; -} - -/* add paging to paging queue(s) */ -int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - uint8_t l, trx, ts, any_tbf = 0; - struct gprs_rlcmac_tbf *tbf; - struct gprs_rlcmac_paging *pag; - uint8_t slot_mask[8]; - int8_t first_ts; /* must be signed */ - - LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n", - chan_needed, osmo_hexdump(identity_lv + 1, identity_lv[0])); - - /* collect slots to page - * Mark slots for every TBF, but only mark one of it. - * Mark only the first slot found. - * Don't mark, if TBF uses a different slot that is already marked. */ - memset(slot_mask, 0, sizeof(slot_mask)); - for (l = 0; gprs_rlcmac_tbfs_lists[l]; l++) { - llist_for_each_entry(tbf, gprs_rlcmac_tbfs_lists[l], list) { - first_ts = -1; - for (ts = 0; ts < 8; ts++) { - if (tbf->pdch[ts]) { - /* remember the first slot found */ - if (first_ts < 0) - first_ts = ts; - /* break, if we already marked a slot */ - if ((slot_mask[tbf->trx] & (1 << ts))) - break; - } - } - /* mark first slot found, if none is marked already */ - if (ts == 8 && first_ts >= 0) { - LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses " - "TRX=%d TS=%d, so we mark\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) - ? "UL" : "DL", - tbf->tfi, tbf->trx, first_ts); - slot_mask[tbf->trx] |= (1 << first_ts); - } else - LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses " - "already marked TRX=%d TS=%d\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) - ? "UL" : "DL", - tbf->tfi, tbf->trx, ts); - } - } - - /* Now we have a list of marked slots. Every TBF uses at least one - * of these slots. */ - - /* schedule paging to all marked slots */ - for (trx = 0; trx < 8; trx++) { - if (slot_mask[trx] == 0) - continue; - any_tbf = 1; - for (ts = 0; ts < 8; ts++) { - if ((slot_mask[trx] & (1 << ts))) { - /* schedule */ - pag = talloc_zero(tall_pcu_ctx, - struct gprs_rlcmac_paging); - if (!pag) - return -ENOMEM; - pag->chan_needed = chan_needed; - memcpy(pag->identity_lv, identity_lv, - identity_lv[0] + 1); - llist_add(&pag->list, - &bts->trx[trx].pdch[ts].paging_list); - LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of " - "TRX=%d TS=%d\n", trx, ts); - } - } - } - - if (!any_tbf) - LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n"); - - return 0; -} - -struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging( - struct gprs_rlcmac_pdch *pdch) -{ - struct gprs_rlcmac_paging *pag; - - if (llist_empty(&pdch->paging_list)) - return NULL; - pag = llist_entry(pdch->paging_list.next, - struct gprs_rlcmac_paging, list); - llist_del(&pag->list); - - return pag; -} - -struct msgb *gprs_rlcmac_send_packet_paging_request( - struct gprs_rlcmac_pdch *pdch) -{ - struct gprs_rlcmac_paging *pag; - struct msgb *msg; - unsigned wp = 0, len; - - /* no paging, no message */ - pag = gprs_rlcmac_dequeue_paging(pdch); - if (!pag) - return NULL; - - LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n"); - - /* alloc message */ - msg = msgb_alloc(23, "pag ctrl block"); - if (!msg) { - talloc_free(pag); - return NULL; - } - bitvec *pag_vec = bitvec_alloc(23); - if (!pag_vec) { - msgb_free(msg); - talloc_free(pag); - return NULL; - } - wp = write_packet_paging_request(pag_vec); - - /* loop until message is full */ - while (pag) { - /* try to add paging */ - if ((pag->identity_lv[1] & 0x07) == 4) { - /* TMSI */ - LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n", - ntohl(*((uint32_t *)(pag->identity_lv + 1)))); - len = 1 + 1 + 1 + 32 + 2 + 1; - if (pag->identity_lv[0] != 5) { - LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with " - "MI != 5 octets!\n"); - goto continue_next; - } - } else { - /* MI */ - LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n", - osmo_hexdump(pag->identity_lv + 1, - pag->identity_lv[0])); - len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1; - if (pag->identity_lv[0] > 8) { - LOGP(DRLCMAC, LOGL_ERROR, "Paging with " - "MI > 8 octets!\n"); - goto continue_next; - } - } - if (wp + len > 184) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule " - "next time\n"); - /* put back paging record, because does not fit */ - llist_add_tail(&pag->list, &pdch->paging_list); - break; - } - write_repeated_page_info(pag_vec, wp, pag->identity_lv[0], - pag->identity_lv + 1, pag->chan_needed); - -continue_next: - talloc_free(pag); - pag = gprs_rlcmac_dequeue_paging(pdch); - } - - bitvec_pack(pag_vec, msgb_put(msg, 23)); - RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); - LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n"); - decode_gsm_rlcmac_downlink(pag_vec, mac_control_block); - LOGPC(DCSN1, LOGL_NOTICE, "\n"); - LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n"); - bitvec_free(pag_vec); - talloc_free(mac_control_block); - - return msg; -} - -// GSM 04.08 9.1.18 Immediate assignment -int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra, - uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, - uint8_t tfi, uint8_t usf, uint32_t tlli, - uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha, - uint8_t gamma, int8_t ta_idx) -{ - unsigned wp = 0; - uint8_t plen; - - bitvec_write_field(dest, wp,0x0,4); // Skip Indicator - bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator - bitvec_write_field(dest, wp,0x3F,8); // Immediate Assignment Message Type - - // 10.5.2.25b Dedicated mode or TBF - bitvec_write_field(dest, wp,0x0,1); // spare - bitvec_write_field(dest, wp,0x0,1); // TMA : Two-message assignment: No meaning - bitvec_write_field(dest, wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode - bitvec_write_field(dest, wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF). - - bitvec_write_field(dest, wp,0x0,4); // Page Mode - - // GSM 04.08 10.5.2.25a Packet Channel Description - bitvec_write_field(dest, wp,0x1,5); // Channel type - bitvec_write_field(dest, wp,ts,3); // TN - bitvec_write_field(dest, wp,tsc,3); // TSC - bitvec_write_field(dest, wp,0x0,3); // non-hopping RF channel configuraion - bitvec_write_field(dest, wp,arfcn,10); // ARFCN - - //10.5.2.30 Request Reference - bitvec_write_field(dest, wp,ra,8); // RA - bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1' - bitvec_write_field(dest, wp,ref_fn % 51,6); // T3 - bitvec_write_field(dest, wp,ref_fn % 26,5); // T2 - - // 10.5.2.40 Timing Advance - bitvec_write_field(dest, wp,0x0,2); // spare - bitvec_write_field(dest, wp,ta,6); // Timing Advance value - - // No mobile allocation in non-hopping systems. - // A zero-length LV. Just write L=0. - bitvec_write_field(dest, wp,0,8); - - if ((wp % 8)) { - LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS without rest " - "octets is not multiple of 8 bits, PLEASE FIX!\n"); - exit (0); - } - plen = wp / 8; - - if (downlink) - { - // GSM 04.08 10.5.2.16 IA Rest Octets - bitvec_write_field(dest, wp, 3, 2); // "HH" - bitvec_write_field(dest, wp, 1, 2); // "01" Packet Downlink Assignment - bitvec_write_field(dest, wp,tlli,32); // TLLI - bitvec_write_field(dest, wp,0x1,1); // switch TFI : on - bitvec_write_field(dest, wp,tfi,5); // TFI - bitvec_write_field(dest, wp,0x0,1); // RLC acknowledged mode - if (alpha) { - bitvec_write_field(dest, wp,0x1,1); // ALPHA = present - bitvec_write_field(dest, wp,alpha,4); // ALPHA - } else { - bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present - } - bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter - bitvec_write_field(dest, wp,polling,1); // Polling Bit - bitvec_write_field(dest, wp,!polling,1); // TA_VALID ??? - if (ta_idx < 0) { - bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off - } else { - bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on - bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX - } - if (polling) { - bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present - bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1' - bitvec_write_field(dest, wp,fn % 51,6); // T3 - bitvec_write_field(dest, wp,fn % 26,5); // T2 - } else { - bitvec_write_field(dest, wp,0x0,1); // TBF Starting TIME present - } - bitvec_write_field(dest, wp,0x0,1); // P0 not present -// bitvec_write_field(dest, wp,0x1,1); // P0 not present -// bitvec_write_field(dest, wp,0xb,4); - } - else - { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - // GMS 04.08 10.5.2.37b 10.5.2.16 - bitvec_write_field(dest, wp, 3, 2); // "HH" - bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment - if (single_block) { - bitvec_write_field(dest, wp, 0, 1); // Block Allocation : Single Block Allocation - if (alpha) { - bitvec_write_field(dest, wp,0x1,1); // ALPHA = present - bitvec_write_field(dest, wp,alpha,4); // ALPHA = present - } else - bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present - bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter - if (ta_idx < 0) { - bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off - } else { - bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on - bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX - } - bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG - bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1' - bitvec_write_field(dest, wp,fn % 51,6); // T3 - bitvec_write_field(dest, wp,fn % 26,5); // T2 - } else { - bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation - bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity - bitvec_write_field(dest, wp, 0, 1); // POLLING - bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic - bitvec_write_field(dest, wp, usf, 3); // USF - bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY - bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present - bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND - bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING - if (alpha) { - bitvec_write_field(dest, wp,0x1,1); // ALPHA = present - bitvec_write_field(dest, wp,alpha,4); // ALPHA - } else - bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present - bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter - /* note: there is no choise for TAI and no starting time */ - bitvec_write_field(dest, wp, 0, 1); // switch TIMING_ADVANCE_INDEX = off - bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG - } - } - - return plen; -} - -/* generate uplink assignment */ -void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi, - uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, - struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha, - uint8_t gamma, int8_t ta_idx) -{ - // TODO We should use our implementation of encode RLC/MAC Control messages. - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - unsigned wp = 0; - uint8_t ts; - - bitvec_write_field(dest, wp,0x1,2); // Payload Type - bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13) - bitvec_write_field(dest, wp,poll,1); // Suppl/Polling Bit - bitvec_write_field(dest, wp,0x0,3); // Uplink state flag - bitvec_write_field(dest, wp,0xa,6); // MESSAGE TYPE - - bitvec_write_field(dest, wp,0x0,2); // Page Mode - - bitvec_write_field(dest, wp,0x0,1); // switch PERSIST_LEVEL: off - if (use_tlli) { - bitvec_write_field(dest, wp,0x2,2); // switch TLLI : on - bitvec_write_field(dest, wp,tlli,32); // TLLI - } else { - bitvec_write_field(dest, wp,0x0,1); // switch TFI : on - bitvec_write_field(dest, wp,old_downlink,1); // 0=UPLINK TFI, 1=DL TFI - bitvec_write_field(dest, wp,old_tfi,5); // TFI - } - - bitvec_write_field(dest, wp,0x0,1); // Message escape - bitvec_write_field(dest, wp,bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND - bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING - bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on - bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE - if (ta_idx < 0) { - bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off - } else { - bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on - bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX - } - -#if 1 - bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present - bitvec_write_field(dest, wp,tbf->tsc,3); // Training Sequence Code (TSC) - bitvec_write_field(dest, wp,0x0,2); // ARFCN = present - bitvec_write_field(dest, wp,tbf->arfcn,10); // ARFCN -#else - bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off -#endif - - bitvec_write_field(dest, wp,0x1,2); // Dynamic Allocation - - bitvec_write_field(dest, wp,0x0,1); // Extended Dynamic Allocation = off - bitvec_write_field(dest, wp,0x0,1); // P0 = off - - bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY - bitvec_write_field(dest, wp,0x1,1); // switch TFI : on - bitvec_write_field(dest, wp,tbf->tfi,5);// TFI - - bitvec_write_field(dest, wp,0x0,1); // - bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off - if (alpha || gamma) { - bitvec_write_field(dest, wp,0x1,1); // Timeslot Allocation with Power Control - bitvec_write_field(dest, wp,alpha,4); // ALPHA - } else - bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation - - for (ts = 0; ts < 8; ts++) { - if (tbf->pdch[ts]) { - bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on - bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i) - if (alpha || gamma) - bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter - } else - bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off - } -// bitvec_write_field(dest, wp,0x0,1); // Measurement Mapping struct not present -} - - -/* generate downlink assignment */ -void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi, - uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll, - uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts) -{ - // Packet downlink assignment TS 44.060 11.2.7 - - uint8_t tn; - - block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header - block->RRBP = 0x0; // N+13 - block->SP = poll; // RRBP field is valid - block->USF = 0x0; // Uplink state flag - - block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2; // Packet Downlink Assignment - block->u.Packet_Downlink_Assignment.PAGE_MODE = 0x0; // Normal Paging - - block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL = 0x0; // PERSISTENCE_LEVEL: off - - block->u.Packet_Downlink_Assignment.ID.UnionType = 0x0; // TFI = on - block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType = old_downlink; // 0=UPLINK TFI, 1=DL TFI - block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi; // TFI - - block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation - block->u.Packet_Downlink_Assignment.RLC_MODE = 0x0; // RLC acknowledged mode - block->u.Packet_Downlink_Assignment.CONTROL_ACK = old_downlink; // NW establishes no new DL TBF for the MS with running timer T3192 - block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s) - for (tn = 0; tn < 8; tn++) { - if (tbf->pdch[tn]) - block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s) - } - - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta; // TIMING_ADVANCE_VALUE - if (ta_idx < 0) { - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off - } else { - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x1; // TIMING_ADVANCE_INDEX = on - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_INDEX = ta_idx; // TIMING_ADVANCE_INDEX - block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_TIMESLOT_NUMBER = ta_ts; // TIMING_ADVANCE_TS - } - - block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off - - block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on - block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tbf->tsc; // Training Sequence Code (TSC) - block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0; // ARFCN = on - block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = tbf->arfcn; // ARFCN - - block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on - block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi; // TFI - - block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on - block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA - - for (tn = 0; tn < 8; tn++) - { - if (tbf->pdch[tn]) - { - block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on - block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN - } - else - { - block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off - } - } - - block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time = 0x0; // TBF Starting TIME = off - block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off - block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off -} - -/* generate paging request */ -int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len) -{ - unsigned wp = 0; - int plen; - - bitvec_write_field(dest, wp,0x0,4); // Skip Indicator - bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator - bitvec_write_field(dest, wp,0x21,8); // Paging Request Message Type - - bitvec_write_field(dest, wp,0x0,4); // Page Mode - bitvec_write_field(dest, wp,0x0,4); // Channel Needed - - // Mobile Identity - bitvec_write_field(dest, wp,ptmsi_len+1,8); // Mobile Identity length - bitvec_write_field(dest, wp,0xf,4); // unused - bitvec_write_field(dest, wp,0x4,4); // PTMSI type - for (int i = 0; i < ptmsi_len; i++) - { - bitvec_write_field(dest, wp,ptmsi[i],8); // PTMSI - } - if ((wp % 8)) { - LOGP(DRLCMACUL, LOGL_ERROR, "Length of PAG.REQ without rest " - "octets is not multiple of 8 bits, PLEASE FIX!\n"); - exit (0); - } - plen = wp / 8; - bitvec_write_field(dest, wp,0x0,1); // "L" NLN(PCH) = off - bitvec_write_field(dest, wp,0x0,1); // "L" Priority1 = off - bitvec_write_field(dest, wp,0x1,1); // "L" Priority2 = off - bitvec_write_field(dest, wp,0x0,1); // "L" Group Call information = off - bitvec_write_field(dest, wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure - bitvec_write_field(dest, wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure - - return plen; -} - -/* generate uplink ack */ -void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf, - uint8_t final) -{ - // Packet Uplink Ack/Nack TS 44.060 11.2.28 - - char show_v_n[65]; - - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - uint8_t rbb = 0; - uint16_t i, bbn; - uint16_t mod_sns_half = (tbf->sns >> 1) - 1; - char bit; - - LOGP(DRLCMACUL, LOGL_DEBUG, "Sending Ack/Nack for TBF=%d " - "(final=%d)\n", tbf->tfi, final); - - block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header - block->RRBP = 0x0; // N+13 - block->SP = final; // RRBP field is valid, if it is final ack - block->USF = 0x0; // Uplink state flag - - block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9; // Packet Downlink Assignment - block->u.Packet_Uplink_Ack_Nack.PAGE_MODE = 0x0; // Normal Paging - block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi; // Uplink TFI - - block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = bts->initial_cs_ul - 1; // CS1 - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.v_r; // STARTING_SEQUENCE_NUMBER - // RECEIVE_BLOCK_BITMAP - for (i = 0, bbn = (tbf->dir.ul.v_r - 64) & mod_sns_half; i < 64; - i++, bbn = (bbn + 1) & mod_sns_half) { - bit = tbf->dir.ul.v_n[bbn]; - if (bit == 0) - bit = ' '; - show_v_n[i] = bit; - if (bit == 'R') - rbb = (rbb << 1)|1; - else - rbb = (rbb << 1); - if((i%8) == 7) - { - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP[i/8] = rbb; - rbb = 0; - } - } - show_v_n[64] = '\0'; - LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received " - "N=Not-Received\n", show_v_n); - - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType = 0x0; // Fixed Allocation Dummy = on - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.u.FixedAllocationDummy = 0x0; // Fixed Allocation Dummy - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off - - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_CONTENTION_RESOLUTION_TLLI = 0x1; - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI = tbf->tlli; - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Packet_Timing_Advance = 0x0; - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Extension_Bits = 0x0; - block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0; -} - -unsigned write_packet_paging_request(bitvec * dest) -{ - unsigned wp = 0; - - bitvec_write_field(dest, wp,0x1,2); // Payload Type - bitvec_write_field(dest, wp,0x0,3); // No polling - bitvec_write_field(dest, wp,0x0,3); // Uplink state flag - bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE - - bitvec_write_field(dest, wp,0x0,2); // Page Mode - - bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL - bitvec_write_field(dest, wp,0x0,1); // No NLN - - return wp; -} - -unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len, - uint8_t *identity, uint8_t chan_needed) -{ - bitvec_write_field(dest, wp,0x1,1); // Repeated Page info exists - - bitvec_write_field(dest, wp,0x1,1); // RR connection paging - - if ((identity[0] & 0x07) == 4) { - bitvec_write_field(dest, wp,0x0,1); // TMSI - identity++; - len--; - } else { - bitvec_write_field(dest, wp,0x0,1); // MI - bitvec_write_field(dest, wp,len,4); // MI len - } - while (len) { - bitvec_write_field(dest, wp,*identity++,8); // MI data - len--; - } - bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED - bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY - - return wp; -} - -/* Send Uplink unit-data to SGSN. */ -int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf) -{ - uint8_t qos_profile[3]; - struct msgb *llc_pdu; - unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->llc_index; - struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); - - LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x len=%d\n", tbf->tfi, tbf->tlli, tbf->llc_index); - if (!bctx) { - LOGP(DBSSGP, LOGL_ERROR, "No bctx\n"); - return -EIO; - } - - llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu"); - uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*tbf->llc_index)); - tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->llc_index, tbf->llc_frame); - qos_profile[0] = QOS_PROFILE >> 16; - qos_profile[1] = QOS_PROFILE >> 8; - qos_profile[2] = QOS_PROFILE; - bssgp_tx_ul_ud(bctx, tbf->tlli, qos_profile, llc_pdu); - - return 0; -} - int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len, const char *imsi) { LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n"); bitvec *paging_request = bitvec_alloc(23); bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - int plen = write_paging_request(paging_request, ptmsi, ptmsi_len); + int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len); pcu_l1if_tx_pch(paging_request, plen, (char *)imsi); bitvec_free(paging_request); @@ -1474,105 +43,3 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len, } -/* - * timing advance memory - */ - -/* enable to debug timing advance memory */ -//#define DEBUG_TA - -static LLIST_HEAD(gprs_rlcmac_ta_list); -static int gprs_rlcmac_ta_num = 0; - -struct gprs_rlcmac_ta { - struct llist_head list; - uint32_t tlli; - uint8_t ta; -}; - -/* remember timing advance of a given TLLI */ -int remember_timing_advance(uint32_t tlli, uint8_t ta) -{ - struct gprs_rlcmac_ta *ta_entry; - - /* check for existing entry */ - llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) { - if (ta_entry->tlli == tlli) { -#ifdef DEBUG_TA - fprintf(stderr, "update %08x %d\n", tlli, ta); -#endif - ta_entry->ta = ta; - /* relink to end of list */ - llist_del(&ta_entry->list); - llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list); - return 0; - } - } - -#ifdef DEBUG_TA - fprintf(stderr, "remember %08x %d\n", tlli, ta); -#endif - /* if list is full, remove oldest entry */ - if (gprs_rlcmac_ta_num == 30) { - ta_entry = llist_entry(gprs_rlcmac_ta_list.next, - struct gprs_rlcmac_ta, list); - llist_del(&ta_entry->list); - talloc_free(ta_entry); - gprs_rlcmac_ta_num--; - } - - /* create new TA entry */ - ta_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ta); - if (!ta_entry) - return -ENOMEM; - - ta_entry->tlli = tlli; - ta_entry->ta = ta; - llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list); - gprs_rlcmac_ta_num++; - - return 0; -} - -int recall_timing_advance(uint32_t tlli) -{ - struct gprs_rlcmac_ta *ta_entry; - uint8_t ta; - - llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) { - if (ta_entry->tlli == tlli) { - ta = ta_entry->ta; -#ifdef DEBUG_TA - fprintf(stderr, "recall %08x %d\n", tlli, ta); -#endif - return ta; - } - } -#ifdef DEBUG_TA - fprintf(stderr, "no entry for %08x\n", tlli); -#endif - - return -EINVAL; -} - -int flush_timing_advance(void) -{ - struct gprs_rlcmac_ta *ta_entry; - int count = 0; - - while (!llist_empty(&gprs_rlcmac_ta_list)) { - ta_entry = llist_entry(gprs_rlcmac_ta_list.next, - struct gprs_rlcmac_ta, list); -#ifdef DEBUG_TA - fprintf(stderr, "flush entry %08x %d\n", ta_entry->tlli, - ta_entry->ta); -#endif - llist_del(&ta_entry->list); - talloc_free(ta_entry); - count++; - } - gprs_rlcmac_ta_num = 0; - - return count; -} - diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index 8863af47..148250ae 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -32,68 +32,16 @@ extern "C" { } #endif -/* generate a diagram for debugging timing issues */ -//#define DEBUG_DIAGRAM - /* This special feature will delay assignment of downlink TBF by one second, * in case there is already a TBF. * This is usefull to debug downlink establishment during packet idle mode. */ //#define DEBUG_DL_ASS_IDLE -/* - * PDCH instanc - */ struct gprs_rlcmac_tbf; - -struct gprs_rlcmac_pdch { - uint8_t enable; /* TS is enabled */ - uint8_t tsc; /* TSC of this slot */ - uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */ - uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */ - struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */ - struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */ - struct llist_head paging_list; /* list of paging messages */ - uint32_t last_rts_fn; /* store last frame number of RTS */ -}; - -struct gprs_rlcmac_trx { - void *fl1h; - uint16_t arfcn; - struct gprs_rlcmac_pdch pdch[8]; - struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */ - struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */ -}; - -struct gprs_rlcmac_bts { - uint8_t bsic; - uint8_t fc_interval; - uint8_t cs1; - uint8_t cs2; - uint8_t cs3; - uint8_t cs4; - uint8_t initial_cs_dl, initial_cs_ul; - uint8_t force_cs; /* 0=use from BTS 1=use from VTY */ - uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */ - uint8_t t3142; - uint8_t t3169; - uint8_t t3191; - uint16_t t3193_msec; - uint8_t t3195; - uint8_t n3101; - uint8_t n3103; - uint8_t n3105; - struct gprs_rlcmac_trx trx[8]; - int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, - struct gprs_rlcmac_tbf *old_tbf, - struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single); - uint32_t alloc_algorithm_curst; /* options to customize algorithm */ - uint8_t force_two_phase; - uint8_t alpha, gamma; -}; - -extern struct gprs_rlcmac_bts *gprs_rlcmac_bts; +struct gprs_rlcmac_bts; +struct BTS; #ifdef __cplusplus /* @@ -106,17 +54,6 @@ struct gprs_rlcmac_paging { }; /* - * single block allocation entry - */ -struct gprs_rlcmac_sba { - struct llist_head list; - uint8_t trx; - uint8_t ts; - uint32_t fn; - uint8_t ta; -}; - -/* * coding scheme info */ struct gprs_rlcmac_cs { @@ -125,14 +62,6 @@ struct gprs_rlcmac_cs { uint8_t block_payload; }; -extern struct gprs_rlcmac_cs gprs_rlcmac_cs[]; - -#ifdef DEBUG_DIAGRAM -void debug_diagram(int diag, const char *format, ...); -#else -#define debug_diagram(a, b, args...) ; -#endif - int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, uint16_t lost); @@ -146,10 +75,6 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf); int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets); -int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta); - -struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn); - /* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */ enum gprs_rlcmac_block_type { GPRS_RLCMAC_DATA_BLOCK = 0x0, @@ -158,88 +83,15 @@ enum gprs_rlcmac_block_type { GPRS_RLCMAC_RESERVED = 0x3 }; -int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len, - uint32_t fn, int8_t rssi); - -int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra, - uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, - uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling, - uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma, - int8_t ta_idx); - -void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi, - uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, - struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha, - uint8_t gamma, int8_t ta_idx); - -void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi, - uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll, - uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts); - - - -void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf, - uint8_t final); - int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf); -int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf); - -int gprs_rlcmac_sba_timeout(struct gprs_rlcmac_sba *sba); - -int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta); - -int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts, - uint32_t fn); - -struct msgb *gprs_rlcmac_send_packet_uplink_assignment( - struct gprs_rlcmac_tbf *tbf, uint32_t fn); - -struct msgb *gprs_rlcmac_send_packet_downlink_assignment( - struct gprs_rlcmac_tbf *tbf, uint32_t fn); - -void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf, - struct gprs_rlcmac_tbf *old_tbf, const char *imsi); - -int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, - uint8_t ssn, uint8_t *rbb); - int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len, const char *imsi); -unsigned write_packet_paging_request(bitvec * dest); - -unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len, - uint8_t *identity, uint8_t chan_needed); - -int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts, - uint8_t *data, uint8_t len, int8_t rssi); - -struct msgb *gprs_rlcmac_send_data_block_acknowledged( - struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts); - -struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf, - uint32_t fn); - -int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, +int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts, + uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr); -int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn); - -int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv); - -struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging( - struct gprs_rlcmac_pdch *pdch); - -struct msgb *gprs_rlcmac_send_packet_paging_request( - struct gprs_rlcmac_pdch *pdch); - -int remember_timing_advance(uint32_t tlli, uint8_t ta); - -int recall_timing_advance(uint32_t tlli); - -int flush_timing_advance(void); - extern "C" { #endif int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp deleted file mode 100644 index 17c4b1b3..00000000 --- a/src/gprs_rlcmac_data.cpp +++ /dev/null @@ -1,1826 +0,0 @@ -/* Data block transfer - * - * Copyright (C) 2012 Ivan Klyuchnikov - * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <gprs_bssgp_pcu.h> -#include <gprs_rlcmac.h> -#include <pcu_l1_if.h> -#include <tbf.h> - -extern void *tall_pcu_ctx; - -extern "C" { -int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, - uint8_t num_frames, uint32_t num_octets); -} - -/* After receiving these frames, we send ack/nack. */ -#define SEND_ACK_AFTER_FRAMES 20 - -/* After sending these frames, we poll for ack/nack. */ -#define POLL_ACK_AFTER_FRAMES 20 - -/* If acknowledgement to uplink/downlink assignmentshould be polled */ -#define POLLING_ASSIGNMENT_DL 1 -#define POLLING_ASSIGNMENT_UL 1 - -extern "C" { -/* TS 04.60 10.2.2 */ -struct rlc_ul_header { - uint8_t r:1, - si:1, - cv:4, - pt:2; - uint8_t ti:1, - tfi:5, - pi:1, - spare:1; - uint8_t e:1, - bsn:7; -} __attribute__ ((packed)); - -struct rlc_dl_header { - uint8_t usf:3, - s_p:1, - rrbp:2, - pt:2; - uint8_t fbi:1, - tfi:5, - pr:2; - uint8_t e:1, - bsn:7; -} __attribute__ ((packed)); - -struct rlc_li_field { - uint8_t e:1, - m:1, - li:6; -} __attribute__ ((packed)); -} - -static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll, - const char *imsi); - -static int gprs_rlcmac_diag(struct gprs_rlcmac_tbf *tbf) -{ - return tbf->rlcmac_diag(); -} - -int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf) -{ - LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for %s TBF=%d\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi); - - tbf->poll_state = GPRS_RLCMAC_POLL_NONE; - - if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { - if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) { - LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " - "PACKET CONTROL ACK for PACKET UPLINK ACK\n"); - gprs_rlcmac_diag(tbf); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK); - } - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; - debug_diagram(tbf->diag, "timeout UL-ACK"); - if (tbf->state_is(GPRS_RLCMAC_FINISHED)) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - - tbf->dir.ul.n3103++; - if (tbf->dir.ul.n3103 == bts->n3103) { - LOGP(DRLCMAC, LOGL_NOTICE, - "- N3103 exceeded\n"); - debug_diagram(tbf->diag, "N3103 exceeded"); - tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); - tbf_timer_start(tbf, 3169, bts->t3169, 0); - return 0; - } - /* reschedule UL ack */ - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; - } - } else - if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - - if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { - LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " - "PACKET CONTROL ACK for PACKET UPLINK " - "ASSIGNMENT.\n"); - gprs_rlcmac_diag(tbf); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS); - } - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; - debug_diagram(tbf->diag, "timeout UL-ASS"); - tbf->n3105++; - if (tbf->n3105 == bts->n3105) { - LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - debug_diagram(tbf->diag, "N3105 exceeded"); - tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); - tbf_timer_start(tbf, 3195, bts->t3195, 0); - return 0; - } - /* reschedule UL assignment */ - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; - } else - if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - - if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) { - LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " - "PACKET CONTROL ACK for PACKET DOWNLINK " - "ASSIGNMENT.\n"); - gprs_rlcmac_diag(tbf); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS); - } - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; - debug_diagram(tbf->diag, "timeout DL-ASS"); - tbf->n3105++; - if (tbf->n3105 == bts->n3105) { - LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - debug_diagram(tbf->diag, "N3105 exceeded"); - tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); - tbf_timer_start(tbf, 3195, bts->t3195, 0); - return 0; - } - /* reschedule DL assignment */ - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; - } else - if (tbf->direction == GPRS_RLCMAC_DL_TBF) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - - if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) { - LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " - "PACKET DOWNLINK ACK.\n"); - gprs_rlcmac_diag(tbf); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); - } - debug_diagram(tbf->diag, "timeout DL-ACK"); - tbf->n3105++; - if (tbf->n3105 == bts->n3105) { - LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); - debug_diagram(tbf->diag, "N3105 exceeded"); - tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); - tbf_timer_start(tbf, 3195, bts->t3195, 0); - return 0; - } - /* resend IMM.ASS on CCCH on timeout */ - if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) - && !(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) { - LOGP(DRLCMAC, LOGL_DEBUG, "Re-send dowlink assignment " - "for TBF=%d on PCH (IMSI=%s)\n", tbf->tfi, - tbf->dir.dl.imsi); - /* send immediate assignment */ - gprs_rlcmac_downlink_assignment(tbf, 0, tbf->dir.dl.imsi); - tbf->dir.dl.wait_confirm = 1; - } - } else - LOGP(DRLCMAC, LOGL_ERROR, "- Poll Timeout, but no event!\n"); - - return 0; -} - -int gprs_rlcmac_sba_timeout(struct gprs_rlcmac_sba *sba) -{ - LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n"); - llist_del(&sba->list); - talloc_free(sba); - - return 0; -} - -static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap) -{ - int i; - - for (i = 0; i < cap->Count_MS_RA_capability_value; i++) { - if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability) - continue; - if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class) - continue; - return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class; - } - - return 0; -} - -/* Received Uplink RLC control block. */ -int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts, - uint32_t fn) -{ - int8_t tfi = 0; /* must be signed */ - uint32_t tlli = 0; - struct gprs_rlcmac_tbf *tbf; - struct gprs_rlcmac_sba *sba; - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - int rc; - - RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t); - LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n"); - decode_gsm_rlcmac_uplink(rlc_block, ul_control_block); - LOGPC(DCSN1, LOGL_NOTICE, "\n"); - LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n"); - switch (ul_control_block->u.MESSAGE_TYPE) { - case MT_PACKET_CONTROL_ACK: - tlli = ul_control_block->u.Packet_Control_Acknowledgement.TLLI; - tbf = tbf_by_poll_fn(fn, trx, ts); - if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with " - "unknown FN=%u TLL=0x%08x (TRX %d TS %d)\n", - fn, tlli, trx, ts); - break; - } - tfi = tbf->tfi; - if (tlli != tbf->tlli) { - LOGP(DRLCMAC, LOGL_INFO, "Phone changed TLLI to " - "0x%08x\n", tlli); - tbf->tlli = tlli; - } - LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Control Ack\n", tbf->tfi, tbf->tlli); - tbf->poll_state = GPRS_RLCMAC_POLL_NONE; - - /* check if this control ack belongs to packet uplink ack */ - if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { - LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli); - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; - debug_diagram(tbf->diag, "got CTL-ACK (fin)"); - if ((tbf->state_flags & - (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) { - tbf->state_flags &= - ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ACK); - LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink " - "ack for UL TBF=%d\n", tbf->tfi); - } - tbf_free(tbf); - break; - } - if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) { - LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] DOWNLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli); - /* reset N3105 */ - tbf->n3105 = 0; - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; - debug_diagram(tbf->diag, "got CTL-ACK DL-ASS"); - if (tbf->direction == GPRS_RLCMAC_UL_TBF) - tbf = tbf_by_tlli(tbf->tlli, - GPRS_RLCMAC_DL_TBF); - if (!tbf) { - LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL " - "TBF is gone\n"); - break; - } - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); - /* stop pending assignment timer */ - tbf_timer_stop(tbf); - if ((tbf->state_flags & - (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) { - tbf->state_flags &= - ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS); - LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink " - "assignment for DL TBF=%d\n", tbf->tfi); - } - tbf_assign_control_ts(tbf); - break; - } - if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) { - LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] UPLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli); - /* reset N3105 */ - tbf->n3105 = 0; - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; - debug_diagram(tbf->diag, "got CTL-AC UL-ASS"); - if (tbf->direction == GPRS_RLCMAC_DL_TBF) - tbf = tbf_by_tlli(tbf->tlli, - GPRS_RLCMAC_UL_TBF); - if (!tbf) { - LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL " - "TBF is gone\n"); - break; - } - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); - if ((tbf->state_flags & - (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { - tbf->state_flags &= - ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS); - LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink " - "assignment for UL TBF=%d\n", tbf->tfi); - } - tbf_assign_control_ts(tbf); - break; - } - LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK " - "at no request\n"); - break; - case MT_PACKET_DOWNLINK_ACK_NACK: - tfi = ul_control_block->u.Packet_Downlink_Ack_Nack.DOWNLINK_TFI; - tbf = tbf_by_poll_fn(fn, trx, ts); - if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with " - "unknown FN=%u TFI=%d (TRX %d TS %d)\n", - fn, tfi, trx, ts); - break; - } - if (tbf->tfi != tfi) { - LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with " - "wrong TFI=%d, ignoring!\n", tfi); - break; - } - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK); - if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) { - tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); - LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink ack " - "for DL TBF=%d\n", tbf->tfi); - } - /* reset N3105 */ - tbf->n3105 = 0; - /* stop timer T3191 */ - tbf_timer_stop(tbf); - tlli = tbf->tlli; - LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Downlink Ack/Nack\n", tbf->tfi, tbf->tlli); - tbf->poll_state = GPRS_RLCMAC_POLL_NONE; - debug_diagram(tbf->diag, "got DL-ACK"); - - rc = gprs_rlcmac_downlink_ack(tbf, - ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.FINAL_ACK_INDICATION, - ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER, - ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP); - if (rc == 1) { - tbf_free(tbf); - break; - } - /* check for channel request */ - if (ul_control_block->u.Packet_Downlink_Ack_Nack.Exist_Channel_Request_Description) { - LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack " - "message, so we provide one:\n"); - tbf_alloc_ul(bts, tbf->trx, tbf->ms_class, tbf->tlli, tbf->ta, tbf); - /* schedule uplink assignment */ - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; - } - break; - case MT_PACKET_RESOURCE_REQUEST: - if (ul_control_block->u.Packet_Resource_Request.ID.UnionType) { - tlli = ul_control_block->u.Packet_Resource_Request.ID.u.TLLI; - tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF); - if (tbf) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " - "TLLI=0x%08x while UL TBF=%d still " - "exists. Killing pending DL TBF\n", - tlli, tbf->tfi); - tbf_free(tbf); - tbf = NULL; - } - if (!tbf) { - uint8_t ms_class = 0; - struct gprs_rlcmac_tbf *dl_tbf; - uint8_t ta; - - if ((dl_tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " - "TLLI=0x%08x while DL TBF=%d still exists. " - "Killing pending DL TBF\n", tlli, - dl_tbf->tfi); - tbf_free(dl_tbf); - } - LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF " - "in packet ressource request of single " - "block, so we provide one:\n"); - sba = sba_find(trx, ts, fn); - if (!sba) { - LOGP(DRLCMAC, LOGL_NOTICE, "MS requests UL TBF " - "in packet ressource request of single " - "block, but there is no resource request " - "scheduled!\n"); - rc = recall_timing_advance(tlli); - if (rc >= 0) - ta = rc; - else - ta = 0; - } else { - ta = sba->ta; - remember_timing_advance(tlli, ta); - llist_del(&sba->list); - talloc_free(sba); - } - if (ul_control_block->u.Packet_Resource_Request.Exist_MS_Radio_Access_capability) - ms_class = get_ms_class_by_capability(&ul_control_block->u.Packet_Resource_Request.MS_Radio_Access_capability); - if (!ms_class) - LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n"); - tbf = tbf_alloc_ul(bts, trx, ms_class, tlli, ta, NULL); - if (!tbf) - break; - /* set control ts to current MS's TS, until assignment complete */ - LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts); - tbf->control_ts = ts; - /* schedule uplink assignment */ - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; - debug_diagram(tbf->diag, "Res. REQ"); - break; - } - tfi = tbf->tfi; - } else { - if (ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.UnionType) { - tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.DOWNLINK_TFI; - tbf = tbf_by_tfi(bts, tfi, trx, GPRS_RLCMAC_DL_TBF); - if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TBF=%d\n", tlli); - break; - } - } else { - tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.UPLINK_TFI; - tbf = tbf_by_tfi(bts, tfi, trx, GPRS_RLCMAC_UL_TBF); - if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TBF=%d\n", tlli); - break; - } - } - tlli = tbf->tlli; - } - LOGP(DRLCMAC, LOGL_ERROR, "RX: [PCU <- BTS] %s TFI: %u TLLI: 0x%08x FIXME: Packet ressource request\n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, tbf->tlli); - break; - case MT_PACKET_MEASUREMENT_REPORT: - sba = sba_find(trx, ts, fn); - if (!sba) { - LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement " - "in packet ressource request of single " - "block, but there is no resource request " - "scheduled!\n"); - } else { - remember_timing_advance(ul_control_block->u.Packet_Measurement_Report.TLLI, sba->ta); - llist_del(&sba->list); - talloc_free(sba); - } - gprs_rlcmac_meas_rep(&ul_control_block->u.Packet_Measurement_Report); - break; - default: - LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] unknown control block received\n"); - } - talloc_free(ul_control_block); - return 1; -} - -#ifdef DEBUG_DL_ASS_IDLE - char debug_imsi[16]; -#endif - -/* - * UL data block flow - */ - -/* get TLLI from received UL data block */ -static int tlli_from_ul_data(uint8_t *data, uint8_t len, uint32_t *tlli) -{ - struct rlc_ul_header *rh = (struct rlc_ul_header *)data; - struct rlc_li_field *li; - uint8_t e; - uint32_t _tlli; - - if (!rh->ti) - return -EINVAL; - - data += 3; - len -= 3; - e = rh->e; - /* if E is not set (LI follows) */ - while (!e) { - if (!len) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " - "but no more data\n"); - return -EINVAL; - } - /* get new E */ - li = (struct rlc_li_field *)data; - if (li->e == 0) /* if LI==0, E is interpreted as '1' */ - e = 1; - else - e = li->e; - data++; - len--; - } - if (len < 4) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame " - "border\n"); - return -EINVAL; - } - memcpy(&_tlli, data, 4); - *tlli = ntohl(_tlli); - - return 0; -} - -/* Store received block data in LLC message(s) and forward to SGSN if complete. - */ -static int gprs_rlcmac_assemble_llc(struct gprs_rlcmac_tbf *tbf, uint8_t *data, - uint8_t len) -{ - struct rlc_ul_header *rh = (struct rlc_ul_header *)data; - uint8_t e, m; - struct rlc_li_field *li; - uint8_t frame_offset[16], offset = 0, chunk; - int i, frames = 0; - - LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len); - - data += 3; - len -= 3; - e = rh->e; /* if extended */ - m = 1; /* more frames, that means: the first frame */ - - /* Parse frame offsets from length indicator(s), if any. */ - while (1) { - if (frames == (int)sizeof(frame_offset)) { - LOGP(DRLCMACUL, LOGL_ERROR, "Too many frames in " - "block\n"); - return -EINVAL; - } - frame_offset[frames++] = offset; - LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset " - "%d\n", frames, offset); - if (!len) - break; - /* M == 0 and E == 0 is not allowed in this version. */ - if (!m && !e) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d " - "ignored, because M='0' and E='0'.\n", - tbf->tfi); - return 0; - } - /* no more frames in this segment */ - if (e) { - break; - } - /* There is a new frame and an LI that delimits it. */ - if (m) { - li = (struct rlc_li_field *)data; - LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n", - li->li); - /* Special case: LI == 0 - * If the last segment would fit precisely into the - * rest of the RLC MAC block, there would be no way - * to delimit that this segment ends and is not - * continued in the next block. - * The special LI (0) is used to force the segment to - * extend into the next block, so it is delimited there. - * This LI must be skipped. Also it is the last LI. - */ - if (li->li == 0) { - data++; - len--; - m = 1; /* M is ignored, we know there is more */ - break; /* handle E as '1', so we break! */ - } - e = li->e; - m = li->m; - offset += li->li; - data++; - len--; - continue; - } - } - if (!m) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare " - "data\n"); - } - - LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n", - len); - /* TLLI */ - if (rh->ti) { - if (len < 4) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of " - "frame border\n"); - return -EINVAL; - } - data += 4; - len -= 4; - LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: " - "%d\n", len); - } - - /* PFI */ - if (rh->pi) { - LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, " - "please disable in SYSTEM INFORMATION\n"); - if (len < 1) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA PFI out of " - "frame border\n"); - return -EINVAL; - } - data++; - len--; - LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: " - "%d\n", len); - } - - /* Now we have: - * - a list of frames offsets: frame_offset[] - * - number of frames: i - * - m == 0: Last frame carries spare data (end of TBF). - */ - - /* Check if last offset would exceed frame. */ - if (offset > len) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d ignored, " - "because LI delimits data that exceeds block size.\n", - tbf->tfi); - return -EINVAL; - } - - /* create LLC frames */ - for (i = 0; i < frames; i++) { - /* last frame ? */ - if (i == frames - 1) { - /* no more data in last frame */ - if (!m) - break; - /* data until end of frame */ - chunk = len - frame_offset[i]; - } else { - /* data until next frame */ - chunk = frame_offset[i + 1] - frame_offset[i]; - } - LOGP(DRLCMACUL, LOGL_DEBUG, "-- Appending chunk (len=%d) to " - "frame at %d.\n", chunk, tbf->llc_index); - if (tbf->llc_index + chunk > LLC_MAX_LEN) { - LOGP(DRLCMACUL, LOGL_NOTICE, "LLC frame exceeds " - "maximum size.\n"); - chunk = LLC_MAX_LEN - tbf->llc_index; - } - memcpy(tbf->llc_frame + tbf->llc_index, data + frame_offset[i], - chunk); - tbf->llc_index += chunk; - /* not last frame. */ - if (i != frames - 1) { - /* send frame to SGSN */ - LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for " - "TBF=%d: len=%d\n", tbf->tfi, tbf->llc_index); - gprs_rlcmac_tx_ul_ud(tbf); - tbf->llc_index = 0; /* reset frame space */ - /* also check if CV==0, because the frame may fill up the - * block precisely, then it is also complete. normally the - * frame would be extended into the next block with a 0-length - * delimiter added to this block. */ - } else if (rh->cv == 0) { - /* send frame to SGSN */ - LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for " - "TBF=%d that fits precisely in last block: " - "len=%d\n", tbf->tfi, tbf->llc_index); - gprs_rlcmac_tx_ul_ud(tbf); - tbf->llc_index = 0; /* reset frame space */ - } - } - - return 0; -} - -struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf, - uint32_t fn) -{ - int final = (tbf->state_is(GPRS_RLCMAC_FINISHED)); - struct msgb *msg; - - if (final) { - if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "sheduled for TBF=%d, so we must wait for " - "final uplink ack...\n", tbf->tfi); - return NULL; - } - if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "scheduled for single block allocation...\n"); - return NULL; - } - } - - msg = msgb_alloc(23, "rlcmac_ul_ack"); - if (!msg) - return NULL; - bitvec *ack_vec = bitvec_alloc(23); - if (!ack_vec) { - msgb_free(msg); - return NULL; - } - bitvec_unhex(ack_vec, - "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); - write_packet_uplink_ack(mac_control_block, tbf, final); - encode_gsm_rlcmac_downlink(ack_vec, mac_control_block); - bitvec_pack(ack_vec, msgb_put(msg, 23)); - bitvec_free(ack_vec); - talloc_free(mac_control_block); - - /* now we must set this flag, so we are allowed to assign downlink - * TBF on PACCH. it is only allowed when TLLI is aknowledged. */ - tbf->dir.ul.contention_resolution_done = 1; - - if (final) { - tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; - tbf->poll_fn = (fn + 13) % 2715648; - /* waiting for final acknowledge */ - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; - tbf->dir.ul.final_ack_sent = 1; - } else - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; - debug_diagram(tbf->diag, "send UL-ACK"); - - return msg; -} - -/* receive UL data block - * - * The blocks are defragmented and forwarded as LLC frames, if complete. - */ -int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts, - uint8_t *data, uint8_t len, int8_t rssi) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct gprs_rlcmac_tbf *tbf; - struct rlc_ul_header *rh = (struct rlc_ul_header *)data; - uint16_t mod_sns, mod_sns_half, offset_v_q, offset_v_r, index; - int rc; - - switch (len) { - case 54: - /* omitting spare bits */ - len = 53; - break; - case 40: - /* omitting spare bits */ - len = 39; - break; - case 34: - /* omitting spare bits */ - len = 33; - break; - case 23: - break; - default: - LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid" - "length: %d)\n", len); - return -EINVAL; - } - - /* find TBF inst from given TFI */ - tbf = tbf_by_tfi(bts, rh->tfi, trx, GPRS_RLCMAC_UL_TBF); - if (!tbf) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TBF=%d\n", - rh->tfi); - return 0; - } - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA); - - LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TBF=%d received (V(Q)=%d .. " - "V(R)=%d)\n", rh->tfi, tbf->dir.ul.v_q, tbf->dir.ul.v_r); - - /* process RSSI */ - gprs_rlcmac_rssi(tbf, rssi); - - /* get TLLI */ - if (!tbf->tlli_valid) { - struct gprs_rlcmac_tbf *dl_tbf, *ul_tbf; - - /* no TLLI yet */ - if (!rh->ti) { - LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d without " - "TLLI, but no TLLI received yet\n", rh->tfi); - return 0; - } - rc = tlli_from_ul_data(data, len, &tbf->tlli); - if (rc) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " - "of UL DATA TBF=%d.\n", rh->tfi); - return 0; - } - LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of " - "UL DATA TBF=%d.\n", tbf->tlli, rh->tfi); - if ((dl_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF))) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " - "TLLI=0x%08x while DL TBF=%d still exists. " - "Killing pending DL TBF\n", tbf->tlli, - dl_tbf->tfi); - tbf_free(dl_tbf); - } - /* tbf_by_tlli will not find your TLLI, because it is not - * yet marked valid */ - if ((ul_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF))) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " - "TLLI=0x%08x while UL TBF=%d still exists. " - "Killing pending UL TBF\n", tbf->tlli, - ul_tbf->tfi); - tbf_free(ul_tbf); - } - /* mark TLLI valid now */ - tbf->tlli_valid = 1; - /* store current timing advance */ - remember_timing_advance(tbf->tlli, tbf->ta); - /* already have TLLI, but we stille get another one */ - } else if (rh->ti) { - uint32_t tlli; - rc = tlli_from_ul_data(data, len, &tlli); - if (rc) { - LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " - "of UL DATA TBF=%d.\n", rh->tfi); - return 0; - } - if (tlli != tbf->tlli) { - LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " - "DATA TBF=%d. (Ignoring due to contention " - "resolution)\n", rh->tfi); - return 0; - } - } - - mod_sns = tbf->sns - 1; - mod_sns_half = (tbf->sns >> 1) - 1; - - /* restart T3169 */ - tbf_timer_start(tbf, 3169, bts->t3169, 0); - - /* Increment RX-counter */ - tbf->dir.ul.rx_counter++; - - /* current block relative to lowest unreceived block */ - offset_v_q = (rh->bsn - tbf->dir.ul.v_q) & mod_sns; - /* If out of window (may happen if blocks below V(Q) are received - * again. */ - if (offset_v_q >= tbf->ws) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " - "%d..%d (it's normal)\n", rh->bsn, tbf->dir.ul.v_q, - (tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns); - return 0; - } - /* Write block to buffer and set receive state array. */ - index = rh->bsn & mod_sns_half; /* memory index of block */ - memcpy(tbf->rlc_block[index], data, len); /* Copy block. */ - tbf->rlc_block_len[index] = len; - tbf->dir.ul.v_n[index] = 'R'; /* Mark received block. */ - LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", - rh->bsn, tbf->dir.ul.v_q, - (tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns); - /* Raise V(R) to highest received sequence number not received. */ - offset_v_r = (rh->bsn + 1 - tbf->dir.ul.v_r) & mod_sns; - if (offset_v_r < (tbf->sns >> 1)) { /* Positive offset, so raise. */ - while (offset_v_r--) { - if (offset_v_r) /* all except the received block */ - tbf->dir.ul.v_n[tbf->dir.ul.v_r & mod_sns_half] - = 'N'; /* Mark block as not received */ - tbf->dir.ul.v_r = (tbf->dir.ul.v_r + 1) & mod_sns; - /* Inc V(R). */ - } - LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", - tbf->dir.ul.v_r); - } - - /* Raise V(Q) if possible, and retrieve LLC frames from blocks. - * This is looped until there is a gap (non received block) or - * the window is empty.*/ - while (tbf->dir.ul.v_q != tbf->dir.ul.v_r && tbf->dir.ul.v_n[ - (index = tbf->dir.ul.v_q & mod_sns_half)] == 'R') { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising " - "V(Q) to %d\n", tbf->dir.ul.v_q, - (tbf->dir.ul.v_q + 1) & mod_sns); - /* get LLC data from block */ - gprs_rlcmac_assemble_llc(tbf, tbf->rlc_block[index], - tbf->rlc_block_len[index]); - /* raise V(Q), because block already received */ - tbf->dir.ul.v_q = (tbf->dir.ul.v_q + 1) & mod_sns; - } - - /* Check CV of last frame in buffer */ - if (tbf->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */ - && tbf->dir.ul.v_q == tbf->dir.ul.v_r) { /* if complete */ - struct rlc_ul_header *last_rh = (struct rlc_ul_header *) - tbf->rlc_block[(tbf->dir.ul.v_r - 1) & mod_sns_half]; - LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " - "last block: BSN=%d CV=%d\n", last_rh->bsn, - last_rh->cv); - if (last_rh->cv == 0) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " - "TBF\n"); - tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); - /* Reset N3103 counter. */ - tbf->dir.ul.n3103 = 0; - } - } - - /* If TLLI is included or if we received half of the window, we send - * an ack/nack */ - if (rh->si || rh->ti || tbf->state_is(GPRS_RLCMAC_FINISHED) - || (tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { - if (rh->si) { - LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " - "because MS is stalled.\n"); - } - if (rh->ti) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because TLLI is included.\n"); - } - if (tbf->state_is(GPRS_RLCMAC_FINISHED)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because last block has CV==0.\n"); - } - if ((tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { - LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " - "because %d frames received.\n", - SEND_ACK_AFTER_FRAMES); - } - if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) { -#ifdef DEBUG_DIAGRAM - if (rh->si) - debug_diagram(tbf->diag, "sched UL-ACK stall"); - if (rh->ti) - debug_diagram(tbf->diag, "sched UL-ACK TLLI"); - if (tbf->state_is(GPRS_RLCMAC_FINISHED)) - debug_diagram(tbf->diag, "sched UL-ACK CV==0"); - if ((tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) - debug_diagram(tbf->diag, "sched UL-ACK n=%d", - tbf->dir.ul.rx_counter); -#endif - /* trigger sending at next RTS */ - tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; - } else { - /* already triggered */ - LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is " - "already triggered, don't schedule!\n"); - } - } - - return 0; -} - -struct msgb *gprs_rlcmac_send_packet_uplink_assignment( - struct gprs_rlcmac_tbf *tbf, uint32_t fn) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct msgb *msg; - struct gprs_rlcmac_tbf *new_tbf; - -#if POLLING_ASSIGNMENT_UL == 1 - if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "sheduled for TBF=%d, so we must wait for uplink " - "assignment...\n", tbf->tfi); - return NULL; - } - if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already scheduled for " - "single block allocation...\n"); - return NULL; - } -#endif - - /* on down TBF we get the uplink TBF to be assigned. */ - if (tbf->direction == GPRS_RLCMAC_DL_TBF) - new_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF); - else - new_tbf = tbf; - - if (!new_tbf) { - LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink " - "assignment at downlink TBF=%d, but there is no uplink " - "TBF\n", tbf->tfi); - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; - return NULL; - } - - msg = msgb_alloc(23, "rlcmac_ul_ass"); - if (!msg) - return NULL; - LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Uplink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli); - bitvec *ass_vec = bitvec_alloc(23); - if (!ass_vec) { - msgb_free(msg); - return NULL; - } - bitvec_unhex(ass_vec, - "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - write_packet_uplink_assignment(ass_vec, tbf->tfi, - (tbf->direction == GPRS_RLCMAC_DL_TBF), tbf->tlli, - tbf->tlli_valid, new_tbf, POLLING_ASSIGNMENT_UL, bts->alpha, - bts->gamma, -1); - bitvec_pack(ass_vec, msgb_put(msg, 23)); - RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); - LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Uplink Assignment +++++++++++++++++++++++++\n"); - decode_gsm_rlcmac_downlink(ass_vec, mac_control_block); - LOGPC(DCSN1, LOGL_NOTICE, "\n"); - LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n"); - bitvec_free(ass_vec); - talloc_free(mac_control_block); - -#if POLLING_ASSIGNMENT_UL == 1 - tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; - tbf->poll_fn = (fn + 13) % 2715648; - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK; -#else - tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; - tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW); - tbf_assign_control_ts(new_tbf); -#endif - debug_diagram(tbf->diag, "send UL-ASS"); - - return msg; -} - -int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct gprs_rlcmac_tbf *tbf; - uint8_t trx, ts = 0; - int8_t tfi; /* must be signed */ - uint8_t sb = 0; - uint32_t sb_fn = 0; - int rc; - uint8_t plen; - - LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide " - "one:\n"); - if ((ra & 0xf8) == 0x70) { - LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block " - "allocation\n"); - sb = 1; - } else if (bts->force_two_phase) { - LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, " - "but we force two phase access\n"); - sb = 1; - } - if (qta < 0) - qta = 0; - if (qta > 252) - qta = 252; - if (sb) { - rc = sba_alloc(&trx, &ts, &sb_fn, qta >> 2); - if (rc < 0) - return rc; - LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d " - "ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn, - (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); - LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink " - "(AGCH)\n"); - } else { - // Create new TBF - #warning "Copy and pate with other routines.." - tfi = tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx, -1); - if (tfi < 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); - /* FIXME: send reject */ - return -EBUSY; - } - /* set class to 0, since we don't know the multislot class yet */ - tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx, 0, 1); - if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); - /* FIXME: send reject */ - return -EBUSY; - } - tbf->ta = qta >> 2; - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); - tbf_timer_start(tbf, 3169, bts->t3169, 0); - LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] START TFI: %u\n", - tbf->tfi); - LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u RACH " - "qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n", tbf->tfi, - qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); - LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u Immediate " - "Assignment Uplink (AGCH)\n", tbf->tfi); - } - bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */; - bitvec_unhex(immediate_assignment, - "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - if (sb) - plen = write_immediate_assignment(immediate_assignment, 0, ra, - Fn, qta >> 2, bts->trx[trx].arfcn, ts, - bts->trx[trx].pdch[ts].tsc, 0, 0, 0, 0, sb_fn, 1, - bts->alpha, bts->gamma, -1); - else - plen = write_immediate_assignment(immediate_assignment, 0, ra, - Fn, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc, - tbf->tfi, tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0, - bts->alpha, bts->gamma, -1); - pcu_l1if_tx_agch(immediate_assignment, plen); - bitvec_free(immediate_assignment); - - return 0; -} - - -/* - * DL data block flow - */ - -static struct msgb *llc_dequeue(struct gprs_rlcmac_tbf *tbf) -{ - struct msgb *msg; - struct timeval *tv, tv_now; - uint32_t octets = 0, frames = 0; - - gettimeofday(&tv_now, NULL); - - while ((msg = msgb_dequeue(&tbf->llc_queue))) { - tv = (struct timeval *)msg->data; - msgb_pull(msg, sizeof(*tv)); - if (tv->tv_sec /* not infinite */ - && (tv_now.tv_sec > tv->tv_sec /* and secs expired */ - || (tv_now.tv_sec == tv->tv_sec /* .. or if secs equal .. */ - && tv_now.tv_usec > tv->tv_usec))) { /* .. usecs expired */ - LOGP(DRLCMACDL, LOGL_NOTICE, "Discarding LLC PDU of " - "DL TBF=%d, because lifetime limit reached\n", - tbf->tfi); - frames++; - octets += msg->len; - msgb_free(msg); - continue; - } - break; - } - - if (frames) { - if (frames > 0xff) - frames = 0xff; - if (octets > 0xffffff) - octets = 0xffffff; - bssgp_tx_llc_discarded(gprs_bssgp_pcu_current_bctx(), - tbf->tlli, frames, octets); - } - - return msg; -} - -/* send DL data block - * - * The messages are fragmented and forwarded as data blocks. - */ -struct msgb *gprs_rlcmac_send_data_block_acknowledged( - struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct rlc_dl_header *rh; - struct rlc_li_field *li; - uint8_t block_length; /* total length of block, including spare bits */ - uint8_t block_data; /* usable data of block, w/o spare bits, inc. MAC */ - struct msgb *msg, *dl_msg; - uint8_t bsn; - uint16_t mod_sns = tbf->sns - 1; - uint16_t mod_sns_half = (tbf->sns >> 1) - 1; - uint16_t index; - uint8_t *delimiter, *data, *e_pointer; - uint8_t len; - uint16_t space, chunk; - int first_fin_ack = 0; - - LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. " - "V(S)==%d)\n", tbf->tfi, tbf->dir.dl.v_a, tbf->dir.dl.v_s); - -do_resend: - /* check if there is a block with negative acknowledgement */ - for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; - bsn = (bsn + 1) & mod_sns) { - index = (bsn & mod_sns_half); - if (tbf->dir.dl.v_b[index] == 'N' - || tbf->dir.dl.v_b[index] == 'X') { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", - bsn); - /* re-send block with negative aknowlegement */ - tbf->dir.dl.v_b[index] = 'U'; /* unacked */ - goto tx_block; - } - } - - /* if the window has stalled, or transfer is complete, - * send an unacknowledged block */ - if (tbf->state_is(GPRS_RLCMAC_FINISHED) - || ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns) == tbf->ws) { - int resend = 0; - - if (tbf->state_is(GPRS_RLCMAC_FINISHED)) - LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " - "because all blocks have been transmitted.\n", - tbf->dir.dl.v_a); - else - LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " - "because all window is stalled.\n", - tbf->dir.dl.v_a); - /* If V(S) == V(A) and finished state, we would have received - * acknowledgement of all transmitted block. In this case we - * would have transmitted the final block, and received ack - * from MS. But in this case we did not receive the final ack - * indication from MS. This should never happen if MS works - * correctly. */ - if (tbf->dir.dl.v_s == tbf->dir.dl.v_a) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " - "so we re-transmit final block!\n"); - /* we just send final block again */ - index = ((tbf->dir.dl.v_s - 1) & mod_sns_half); - goto tx_block; - } - - /* cycle through all unacked blocks */ - for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; - bsn = (bsn + 1) & mod_sns) { - index = (bsn & mod_sns_half); - if (tbf->dir.dl.v_b[index] == 'U') { - /* mark to be re-send */ - tbf->dir.dl.v_b[index] = 'X'; - resend++; - } - } - /* At this point there should be at leasst one unacked block - * to be resent. If not, this is an software error. */ - if (resend == 0) { - LOGP(DRLCMACDL, LOGL_ERROR, "Software error: " - "There are no unacknowledged blocks, but V(A) " - " != V(S). PLEASE FIX!\n"); - /* we just send final block again */ - index = ((tbf->dir.dl.v_s - 1) & mod_sns_half); - goto tx_block; - } - goto do_resend; - } - - LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n", - tbf->dir.dl.v_s); - - /* now we still have untransmitted LLC data, so we fill mac block */ - index = tbf->dir.dl.v_s & mod_sns_half; - data = tbf->rlc_block[index]; - if (tbf->cs == 0) { - tbf->cs = bts->initial_cs_dl; - if (tbf->cs < 1 || tbf->cs > 4) - tbf->cs = 1; - } - block_length = gprs_rlcmac_cs[tbf->cs].block_length; - block_data = gprs_rlcmac_cs[tbf->cs].block_data; - memset(data, 0x2b, block_data); /* spare bits will be left 0 */ - rh = (struct rlc_dl_header *)data; - rh->pt = 0; /* Data Block */ - rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */ - rh->usf = 7; /* will be set at scheduler */ - rh->pr = 0; /* FIXME: power reduction */ - rh->tfi = tbf->tfi; /* TFI */ - rh->fbi = 0; /* Final Block Indicator, set late, if true */ - rh->bsn = tbf->dir.dl.v_s; /* Block Sequence Number */ - rh->e = 0; /* Extension bit, maybe set later */ - e_pointer = data + 2; /* points to E of current chunk */ - data += 3; - delimiter = data; /* where next length header would be stored */ - space = block_data - 3; - while (1) { - chunk = tbf->llc_length - tbf->llc_index; - /* if chunk will exceed block limit */ - if (chunk > space) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "larger than space (%d) left in block: copy " - "only remaining space, and we are done\n", - chunk, space); - /* block is filled, so there is no extension */ - *e_pointer |= 0x01; - /* fill only space */ - memcpy(data, tbf->llc_frame + tbf->llc_index, space); - /* incement index */ - tbf->llc_index += space; - /* return data block as message */ - break; - } - /* if FINAL chunk would fit precisely in space left */ - if (chunk == space && llist_empty(&tbf->llc_queue)) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "would exactly fit into space (%d): because " - "this is a final block, we don't add length " - "header, and we are done\n", chunk, space); - LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " - "TBF=%d that fits precisely in last block: " - "len=%d\n", tbf->tfi, tbf->llc_length); - gprs_rlcmac_dl_bw(tbf, tbf->llc_length); - /* block is filled, so there is no extension */ - *e_pointer |= 0x01; - /* fill space */ - memcpy(data, tbf->llc_frame + tbf->llc_index, space); - /* reset LLC frame */ - tbf->llc_index = tbf->llc_length = 0; - /* final block */ - rh->fbi = 1; /* we indicate final block */ - tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); - /* return data block as message */ - break; - } - /* if chunk would fit exactly in space left */ - if (chunk == space) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " - "would exactly fit into space (%d): add length " - "header with LI=0, to make frame extend to " - "next block, and we are done\n", chunk, space); - /* make space for delimiter */ - if (delimiter != data) - memcpy(delimiter + 1, delimiter, - data - delimiter); - data++; - space--; - /* add LI with 0 length */ - li = (struct rlc_li_field *)delimiter; - li->e = 1; /* not more extension */ - li->m = 0; /* shall be set to 0, in case of li = 0 */ - li->li = 0; /* chunk fills the complete space */ - // no need to set e_pointer nor increase delimiter - /* fill only space, which is 1 octet less than chunk */ - memcpy(data, tbf->llc_frame + tbf->llc_index, space); - /* incement index */ - tbf->llc_index += space; - /* return data block as message */ - break; - } - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " - "than remaining space (%d): add length header to " - "to delimit LLC frame\n", chunk, space); - /* the LLC frame chunk ends in this block */ - /* make space for delimiter */ - if (delimiter != data) - memcpy(delimiter + 1, delimiter, data - delimiter); - data++; - space--; - /* add LI to delimit frame */ - li = (struct rlc_li_field *)delimiter; - li->e = 0; /* Extension bit, maybe set later */ - li->m = 0; /* will be set later, if there is more LLC data */ - li->li = chunk; /* length of chunk */ - e_pointer = delimiter; /* points to E of current delimiter */ - delimiter++; - /* copy (rest of) LLC frame to space */ - memcpy(data, tbf->llc_frame + tbf->llc_index, chunk); - data += chunk; - space -= chunk; - LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: " - "len=%d\n", tbf->tfi, tbf->llc_length); - gprs_rlcmac_dl_bw(tbf, tbf->llc_length); - /* reset LLC frame */ - tbf->llc_index = tbf->llc_length = 0; - /* dequeue next LLC frame, if any */ - msg = llc_dequeue(tbf); - if (msg) { - LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " - "TBF=%d (len=%d)\n", tbf->tfi, msg->len); - memcpy(tbf->llc_frame, msg->data, msg->len); - tbf->llc_length = msg->len; - msgb_free(msg); - } - /* if we have more data and we have space left */ - if (space > 0 && tbf->llc_length) { - li->m = 1; /* we indicate more frames to follow */ - continue; - } - /* if we don't have more LLC frames */ - if (!tbf->llc_length) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " - "done.\n"); - li->e = 1; /* we cannot extend */ - rh->fbi = 1; /* we indicate final block */ - first_fin_ack = 1; - /* + 1 indicates: first final ack */ - tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); - break; - } - /* we have no space left */ - LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " - "done.\n"); - li->e = 1; /* we cannot extend */ - break; - } - LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n", - osmo_hexdump(tbf->rlc_block[index], block_length)); - tbf->rlc_block_len[index] = block_length; - /* raise send state and set ack state array */ - tbf->dir.dl.v_b[index] = 'U'; /* unacked */ - tbf->dir.dl.v_s = (tbf->dir.dl.v_s + 1) & mod_sns; /* inc send state */ - -tx_block: - /* from this point on, new block is sent or old block is resent */ - - /* get data and header from current block */ - data = tbf->rlc_block[index]; - len = tbf->rlc_block_len[index]; - rh = (struct rlc_dl_header *)data; - - /* Clear Polling, if still set in history buffer */ - rh->s_p = 0; - - /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx. - */ - if (tbf->dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) { - if (first_fin_ack) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " - "polling, because first final block sent.\n"); - } else { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " - "polling, because %d blocks sent.\n", - POLL_ACK_AFTER_FRAMES); - } - /* scheduling not possible, because: */ - if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) - LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already " - "sheduled for TBF=%d, so we must wait for " - "requesting downlink ack\n", tbf->tfi); - else if (tbf->control_ts != ts) - LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be " - "sheduled in this TS %d, waiting for " - "TS %d\n", ts, tbf->control_ts); - else if (sba_find(tbf->trx, ts, (fn + 13) % 2715648)) - LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be " - "sheduled, because single block alllocation " - "already exists\n"); - else { - LOGP(DRLCMAC, LOGL_DEBUG, "Polling sheduled in this " - "TS %d\n", ts); - tbf->dir.dl.tx_counter = 0; - /* start timer whenever we send the final block */ - if (rh->fbi == 1) - tbf_timer_start(tbf, 3191, bts->t3191, 0); - - /* schedule polling */ - tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; - tbf->poll_fn = (fn + 13) % 2715648; - -#ifdef DEBUG_DIAGRAM - debug_diagram(tbf->diag, "poll DL-ACK"); - if (first_fin_ack) - debug_diagram(tbf->diag, "(is first FINAL)"); - if (rh->fbi) - debug_diagram(tbf->diag, "(FBI is set)"); -#endif - - /* set polling in header */ - rh->rrbp = 0; /* N+13 */ - rh->s_p = 1; /* Polling */ - - /* Increment TX-counter */ - tbf->dir.dl.tx_counter++; - } - } else { - /* Increment TX-counter */ - tbf->dir.dl.tx_counter++; - } - - /* return data block as message */ - dl_msg = msgb_alloc(len, "rlcmac_dl_data"); - if (!dl_msg) - return NULL; - memcpy(msgb_put(dl_msg, len), data, len); - - return dl_msg; -} - -int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, - uint8_t ssn, uint8_t *rbb) -{ - char show_rbb[65], show_v_b[RLC_MAX_SNS + 1]; - uint16_t mod_sns = tbf->sns - 1; - uint16_t mod_sns_half = (tbf->sns >> 1) - 1; - int i; /* must be signed */ - int16_t dist; /* must be signed */ - uint8_t bit; - uint16_t bsn; - struct msgb *msg; - uint16_t lost = 0, received = 0; - - LOGP(DRLCMACDL, LOGL_DEBUG, "TBF=%d downlink acknowledge\n", - tbf->tfi); - - if (!final) { - /* show received array in debug (bit 64..1) */ - for (i = 63; i >= 0; i--) { - bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; - show_rbb[i] = (bit) ? '1' : 'o'; - } - show_rbb[64] = '\0'; - LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" - "(BSN=%d) 1=ACK o=NACK\n", (ssn - 64) & mod_sns, - show_rbb, (ssn - 1) & mod_sns); - - /* apply received array to receive state (SSN-64..SSN-1) */ - /* calculate distance of ssn from V(S) */ - dist = (tbf->dir.dl.v_s - ssn) & mod_sns; - /* check if distance is less than distance V(A)..V(S) */ - if (dist >= ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns)) { - /* this might happpen, if the downlink assignment - * was not received by ms and the ack refers - * to previous TBF - * FIXME: we should implement polling for - * control ack!*/ - LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " - "V(A)..V(S) range (DL TBF=%d) Free TFB!\n", - tbf->tfi); - return 1; /* indicate to free TBF */ - } - /* SSN - 1 is in range V(A)..V(S)-1 */ - for (i = 63, bsn = (ssn - 1) & mod_sns; - i >= 0 && bsn != ((tbf->dir.dl.v_a - 1) & mod_sns); - i--, bsn = (bsn - 1) & mod_sns) { - bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; - if (bit) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- got " - "ack for BSN=%d\n", bsn); - if (tbf->dir.dl.v_b[bsn & mod_sns_half] - != 'A') - received++; - tbf->dir.dl.v_b[bsn & mod_sns_half] = 'A'; - } else { - LOGP(DRLCMACDL, LOGL_DEBUG, "- got " - "NACK for BSN=%d\n", bsn); - tbf->dir.dl.v_b[bsn & mod_sns_half] = 'N'; - lost++; - } - } - /* report lost and received packets */ - gprs_rlcmac_received_lost(tbf, received, lost); - - /* raise V(A), if possible */ - for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; - i++, bsn = (bsn + 1) & mod_sns) { - if (tbf->dir.dl.v_b[bsn & mod_sns_half] == 'A') { - tbf->dir.dl.v_b[bsn & mod_sns_half] = 'I'; - /* mark invalid */ - tbf->dir.dl.v_a = (tbf->dir.dl.v_a + 1) - & mod_sns; - } else - break; - } - - /* show receive state array in debug (V(A)..V(S)-1) */ - for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; - i++, bsn = (bsn + 1) & mod_sns) { - show_v_b[i] = tbf->dir.dl.v_b[bsn & mod_sns_half]; - if (show_v_b[i] == 0) - show_v_b[i] = ' '; - } - show_v_b[i] = '\0'; - LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" - "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " - "X=Resend-Unacked\n", tbf->dir.dl.v_a, show_v_b, - (tbf->dir.dl.v_s - 1) & mod_sns); - - if (tbf->state_is(GPRS_RLCMAC_FINISHED) - && tbf->dir.dl.v_s == tbf->dir.dl.v_a) { - LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " - "all blocks, but without final ack " - "inidcation (don't worry)\n"); - } - return 0; - } - - LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); - debug_diagram(tbf->diag, "got Final ACK"); - /* range V(A)..V(S)-1 */ - for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; - bsn = (bsn + 1) & mod_sns) { - if (tbf->dir.dl.v_b[bsn & mod_sns_half] != 'A') - received++; - } - - /* report all outstanding packets as received */ - gprs_rlcmac_received_lost(tbf, received, lost); - - /* check for LLC PDU in the LLC Queue */ - msg = llc_dequeue(tbf); - if (!msg) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - - /* no message, start T3193, change state to RELEASE */ - LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we " - "release.\n"); - /* start T3193 */ - debug_diagram(tbf->diag, "start T3193"); - tbf_timer_start(tbf, 3193, bts->t3193_msec / 1000, - (bts->t3193_msec % 1000) * 1000); - tbf_new_state(tbf, GPRS_RLCMAC_WAIT_RELEASE); - - return 0; - } - memcpy(tbf->llc_frame, msg->data, msg->len); - tbf->llc_length = msg->len; - msgb_free(msg); - - /* we have a message, so we trigger downlink assignment, and there - * set the state to ASSIGN. also we set old_downlink, because we - * re-use this tbf. */ - LOGP(DRLCMAC, LOGL_DEBUG, "Trigger dowlink assignment on PACCH, " - "because another LLC PDU has arrived in between\n"); - memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset RLC states */ - tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep TO flags */ - tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); - tbf_update(tbf); - gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL); - - return 0; -} - - -struct msgb *gprs_rlcmac_send_packet_downlink_assignment( - struct gprs_rlcmac_tbf *tbf, uint32_t fn) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - struct msgb *msg; - struct gprs_rlcmac_tbf *new_tbf; - int poll_ass_dl = POLLING_ASSIGNMENT_DL; - - if (poll_ass_dl && tbf->direction == GPRS_RLCMAC_DL_TBF - && tbf->control_ts != tbf->first_common_ts) { - LOGP(DRLCMAC, LOGL_NOTICE, "Cannot poll for downlink " - "assigment, because MS cannot reply. (control TS=%d, " - "first common TS=%d)\n", tbf->control_ts, - tbf->first_common_ts); - poll_ass_dl = 0; - } - if (poll_ass_dl) { - if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { - LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already sheduled " - "for TBF=%d, so we must wait for downlink " - "assignment...\n", tbf->tfi); - return NULL; - } - if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) { - LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " - "scheduled for single block allocation...\n"); - return NULL; - } - } - - /* on uplink TBF we get the downlink TBF to be assigned. */ - if (tbf->direction == GPRS_RLCMAC_UL_TBF) { - /* be sure to check first, if contention resolution is done, - * otherwise we cannot send the assignment yet */ - if (!tbf->dir.ul.contention_resolution_done) { - LOGP(DRLCMAC, LOGL_DEBUG, "Cannot assign DL TBF now, " - "because contention resolution is not " - "finished.\n"); - return NULL; - } - new_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF); - } else - new_tbf = tbf; - if (!new_tbf) { - LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink " - "assignment at uplink TBF=%d, but there is no downlink " - "TBF\n", tbf->tfi); - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; - return NULL; - } - - msg = msgb_alloc(23, "rlcmac_dl_ass"); - if (!msg) - return NULL; - bitvec *ass_vec = bitvec_alloc(23); - if (!ass_vec) { - msgb_free(msg); - return NULL; - } - bitvec_unhex(ass_vec, - "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Downlink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli); - RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); - write_packet_downlink_assignment(mac_control_block, tbf->tfi, - (tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf, - poll_ass_dl, bts->alpha, bts->gamma, -1, 0); - LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n"); - encode_gsm_rlcmac_downlink(ass_vec, mac_control_block); - LOGPC(DCSN1, LOGL_NOTICE, "\n"); - LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n"); - bitvec_pack(ass_vec, msgb_put(msg, 23)); - bitvec_free(ass_vec); - talloc_free(mac_control_block); - - if (poll_ass_dl) { - tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; - tbf->poll_fn = (fn + 13) % 2715648; - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK; - } else { - tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; - tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW); - tbf_assign_control_ts(new_tbf); - /* stop pending assignment timer */ - tbf_timer_stop(new_tbf); - - } - debug_diagram(tbf->diag, "send DL-ASS"); - - return msg; -} - -static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll, - const char *imsi) -{ - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; - int plen; - - debug_diagram(tbf->diag, "IMM.ASS (PCH)"); - LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u TLLI: 0x%08x Immediate Assignment Downlink (PCH)\n", tbf->tfi, tbf->tlli); - bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */ - bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); - /* use request reference that has maximum distance to current time, - * so the assignment will not conflict with possible RACH requests. */ - plen = write_immediate_assignment(immediate_assignment, 1, 125, - (tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta, - tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll, - tbf->poll_fn, 0, bts->alpha, bts->gamma, -1); - pcu_l1if_tx_pch(immediate_assignment, plen, imsi); - bitvec_free(immediate_assignment); -} - -/* depending on the current TBF, we assign on PACCH or AGCH */ -void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf, - struct gprs_rlcmac_tbf *old_tbf, const char *imsi) -{ -#ifdef DEBUG_DL_ASS_IDLE - strncpy(debug_imsi, imsi); - LOGP(DRLCMAC, LOGL_ERROR, "**** DEBUGGING DOWNLINK ASSIGNMENT ****\n"); -#endif - - /* stop pending timer */ - tbf_timer_stop(tbf); - - /* check for downlink tbf: */ - if (old_tbf) { -#ifdef DEBUG_DL_ASS_IDLE - LOGP(DRLCMAC, LOGL_ERROR, "We must wait for current TBF to be " - "released.\n"); - /* wait one second until assignment */ - tbf_timer_start(tbf, 1234, 1,0); -#else - LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on " - "PACCH, because %s TBF=%d exists for TLLI=0x%08x\n", - (old_tbf->direction == GPRS_RLCMAC_UL_TBF) - ? "UL" : "DL", old_tbf->tfi, old_tbf->tlli); - old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; - /* use TA from old TBF */ - tbf->ta = old_tbf->ta; - /* change state */ - tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); - /* start timer */ - tbf_timer_start(tbf, 0, Tassign_pacch); -#endif - } else { - LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for TBF=%d on PCH, no TBF exist (IMSI=%s)\n", tbf->tfi, imsi); - if (!imsi || strlen(imsi) < 3) { - LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n"); - return; - } - /* change state */ - tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); - tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); - strncpy(tbf->dir.dl.imsi, imsi, sizeof(tbf->dir.dl.imsi)); - /* send immediate assignment */ - gprs_rlcmac_downlink_assignment(tbf, 0, imsi); - tbf->dir.dl.wait_confirm = 1; - } -} - -int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn) -{ - struct gprs_rlcmac_tbf *tbf; - uint8_t plen; - uint32_t tlli; - - /* move to IA Rest Octets */ - plen = data[0] >> 2; - data += 1 + plen; - - if ((*data & 0xf0) != 0xd0) { - LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest " - "octets do not start with bit sequence 'HH01' " - "(Packet Downlink Assignment)\n"); - return -EINVAL; - } - - /* get TLLI from downlink assignment */ - tlli = (*data++) << 28; - tlli |= (*data++) << 20; - tlli |= (*data++) << 12; - tlli |= (*data++) << 4; - tlli |= (*data++) >> 4; - - tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF); - if (!tbf) { - LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x " - "does not exit\n", tlli); - return -EINVAL; - } - - LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli); - - if (tbf->dir.dl.wait_confirm) { - tbf_timer_start(tbf, 0, Tassign_agch); - } - - return 0; -} - diff --git a/src/gprs_rlcmac_meas.cpp b/src/gprs_rlcmac_meas.cpp index 3229795c..30958fc1 100644 --- a/src/gprs_rlcmac_meas.cpp +++ b/src/gprs_rlcmac_meas.cpp @@ -28,6 +28,7 @@ /* * downlink measurement */ +#warning "TODO: trigger the measurement report from the pollcontroller and use it for flow control" /* received Measurement Report */ int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr) @@ -100,7 +101,7 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf) return -EINVAL; LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n", - tbf->tlli, tbf->meas.rssi_sum / tbf->meas.rssi_num); + tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num); return 0; } @@ -123,7 +124,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, return -EINVAL; LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d " - "Lost: %4d Sum: %4d\n", tbf->tlli, received, lost, sum); + "Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum); tbf->meas.dl_loss_received += received; tbf->meas.dl_loss_lost += lost; @@ -154,7 +155,7 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf) return -EINVAL; LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: " - "%d%%\n", tbf->meas.imsi, tbf->tlli, + "%d%%\n", tbf->imsi(), tbf->tlli(), tbf->meas.dl_loss_lost * 100 / sum); return 0; @@ -179,7 +180,7 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets) return 0; LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: " - "%d KBits/s\n", tbf->meas.imsi, tbf->tlli, + "%d KBits/s\n", tbf->imsi(), tbf->tlli(), tbf->meas.dl_bw_octets / elapsed); /* reset bandwidth values timestamp */ diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp index 476d7812..123e375f 100644 --- a/src/gprs_rlcmac_sched.cpp +++ b/src/gprs_rlcmac_sched.cpp @@ -20,9 +20,11 @@ #include <gprs_bssgp_pcu.h> #include <gprs_rlcmac.h> #include <pcu_l1_if.h> +#include <bts.h> #include <tbf.h> -uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, +static uint32_t sched_poll(struct gprs_rlcmac_bts *bts, + uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, struct gprs_rlcmac_tbf **poll_tbf, struct gprs_rlcmac_tbf **ul_ass_tbf, struct gprs_rlcmac_tbf **dl_ass_tbf, @@ -36,9 +38,9 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, if ((block_nr % 3) == 2) poll_fn ++; poll_fn = poll_fn % 2715648; - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { + llist_for_each_entry(tbf, &bts->ul_tbfs, list) { /* this trx, this ts */ - if (tbf->trx != trx || tbf->control_ts != ts) + if (tbf->trx->trx_no != trx || tbf->control_ts != ts) continue; /* polling for next uplink block */ if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED @@ -50,10 +52,11 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, *dl_ass_tbf = tbf; if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS) *ul_ass_tbf = tbf; +#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?" } - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { + llist_for_each_entry(tbf, &bts->dl_tbfs, list) { /* this trx, this ts */ - if (tbf->trx != trx || tbf->control_ts != ts) + if (tbf->trx->trx_no != trx || tbf->control_ts != ts) continue; /* polling for next uplink block */ if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED @@ -68,31 +71,14 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, return poll_fn; } -uint32_t sched_sba(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr) -{ - uint32_t sba_fn; - struct gprs_rlcmac_sba *sba; - - /* check special TBF for events */ - sba_fn = fn + 4; - if ((block_nr % 3) == 2) - sba_fn ++; - sba_fn = sba_fn % 2715648; - sba = sba_find(trx, ts, sba_fn); - if (sba) - return sba_fn; - - return 0xffffffff; -} - -uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn, +static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, struct gprs_rlcmac_pdch *pdch) { struct gprs_rlcmac_tbf *tbf; uint8_t usf = 0x07; uint8_t i, tfi; - /* select uplink ressource */ + /* select uplink resource */ for (i = 0, tfi = pdch->next_ul_tfi; i < 32; i++, tfi = (tfi + 1) & 31) { tbf = pdch->ul_tbf[tfi]; @@ -110,9 +96,9 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn, usf = tbf->dir.ul.usf[ts]; LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d " "TS=%d FN=%d block_nr=%d scheduling USF=%d for " - "required uplink ressource of UL TBF=%d\n", trx, ts, fn, + "required uplink resource of UL TFI=%d\n", trx, ts, fn, block_nr, usf, tfi); - /* next TBF to handle ressource is the next one */ + /* next TBF to handle resource is the next one */ pdch->next_ul_tfi = (tfi + 1) & 31; break; } @@ -120,7 +106,8 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn, return usf; } -struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn, +static struct msgb *sched_select_ctrl_msg( + uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_tbf *ul_ass_tbf, struct gprs_rlcmac_tbf *dl_ass_tbf, @@ -128,33 +115,40 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn, { struct msgb *msg = NULL; struct gprs_rlcmac_tbf *tbf = NULL; + struct gprs_rlcmac_tbf *next_list[3] = { ul_ass_tbf, dl_ass_tbf, ul_ack_tbf }; - /* schedule PACKET UPLINK ASSIGNMENT (1st priority) */ - if (ul_ass_tbf) { - tbf = ul_ass_tbf; - msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn); - } - /* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */ - if (!msg && dl_ass_tbf) { - tbf = dl_ass_tbf; - msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn); - } - /* schedule PACKET UPLINK ACK (3rd priority) */ - if (!msg && ul_ack_tbf) { - tbf = ul_ack_tbf; - msg = gprs_rlcmac_send_uplink_ack(tbf, fn); + for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) { + tbf = next_list[(pdch->next_ctrl_prio + i) % 3]; + if (!tbf) + continue; + + if (tbf == ul_ass_tbf) + msg = tbf->create_ul_ass(fn); + else if (tbf == dl_ass_tbf) + msg = tbf->create_dl_ass(fn); + else + msg = tbf->create_ul_ack(fn); + + if (!msg) { + tbf = NULL; + continue; + } + + pdch->next_ctrl_prio += i + 1; + pdch->next_ctrl_prio %= 3; + break; } + /* any message */ if (msg) { + tbf->rotate_in_list(); LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control " - "message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) - ? "UL" : "DL", tbf->tfi, trx, ts); + "message at RTS for %s (TRX=%d, TS=%d)\n", + tbf_name(tbf), trx, ts); return msg; } /* schedule PACKET PAGING REQUEST */ - if (!llist_empty(&pdch->paging_list)) - msg = gprs_rlcmac_send_packet_paging_request(pdch); + msg = pdch->packet_paging_request(); if (msg) { LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request " "message at RTS for (TRX=%d, TS=%d)\n", trx, ts); @@ -164,14 +158,15 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn, return NULL; } -struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn, +static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts, + uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr, struct gprs_rlcmac_pdch *pdch) { struct msgb *msg = NULL; struct gprs_rlcmac_tbf *tbf = NULL; uint8_t i, tfi; - /* select downlink ressource */ + /* select downlink resource */ for (i = 0, tfi = pdch->next_dl_tfi; i < 32; i++, tfi = (tfi + 1) & 31) { tbf = pdch->dl_tbf[tfi]; @@ -191,18 +186,18 @@ struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn, continue; LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at " - "RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts); - /* next TBF to handle ressource is the next one */ + "RTS for DL TFI=%d (TRX=%d, TS=%d)\n", tfi, trx, ts); + /* next TBF to handle resource is the next one */ pdch->next_dl_tfi = (tfi + 1) & 31; /* generate DL data block */ - msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn, - ts); + msg = tbf->create_dl_acked_block(fn, ts); break; } return msg; } -static uint8_t rlcmac_dl_idle[23] = { + +static const uint8_t rlcmac_dl_idle[23] = { 0x47, /* control without optional header octets, no polling, USF=111 */ 0x94, /* dummy downlink control message, paging mode 00 */ 0x2b, /* no persistance level, 7 bits spare pattern */ @@ -210,7 +205,7 @@ static uint8_t rlcmac_dl_idle[23] = { 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; -struct msgb *sched_dummy(void) +static struct msgb *sched_dummy(void) { struct msgb *msg; @@ -222,10 +217,10 @@ struct msgb *sched_dummy(void) return msg; } -int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, +int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts, + uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; struct gprs_rlcmac_pdch *pdch; struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL, *ul_ass_tbf = NULL, *ul_ack_tbf = NULL; @@ -233,11 +228,13 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, struct msgb *msg = NULL; uint32_t poll_fn, sba_fn; +#warning "ARFCN... it is already in the TRX..... is it consistent with it?" + if (trx >= 8 || ts >= 8) return -EINVAL; pdch = &bts->trx[trx].pdch[ts]; - if (!pdch->enable) { + if (!pdch->is_enabled()) { LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: " "TRX=%d TS=%d\n", trx, ts); return -EIO; @@ -246,25 +243,24 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, /* store last frame number of RTS */ pdch->last_rts_fn = fn; - poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf, + poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf, &dl_ass_tbf, &ul_ack_tbf); - /* check uplink ressource for polling */ + /* check uplink resource for polling */ if (poll_tbf) LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d " "TS=%d FN=%d block_nr=%d scheduling free USF for " - "polling at FN=%d of %s TFI=%d\n", trx, ts, fn, + "polling at FN=%d of %s\n", trx, ts, fn, block_nr, poll_fn, - (poll_tbf->direction == GPRS_RLCMAC_UL_TBF) - ? "UL" : "DL", poll_tbf->tfi); + tbf_name(poll_tbf)); /* use free USF */ /* else. check for sba */ - else if ((sba_fn = sched_sba(trx, ts, fn, block_nr) != 0xffffffff)) + else if ((sba_fn = bts->bts->sba()->sched(trx, ts, fn, block_nr) != 0xffffffff)) LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d " "TS=%d FN=%d block_nr=%d scheduling free USF for " "single block allocation at FN=%d\n", trx, ts, fn, block_nr, sba_fn); /* use free USF */ - /* else, we search for uplink ressource */ + /* else, we search for uplink resource */ else usf = sched_select_uplink(trx, ts, fn, block_nr, pdch); @@ -274,7 +270,7 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, /* Prio 2: select data message for downlink */ if (!msg) - msg = sched_select_downlink(trx, ts, fn, block_nr, pdch); + msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch); /* Prio 3: send dummy contol message */ if (!msg) diff --git a/src/gprs_rlcmac_ts_alloc.cpp b/src/gprs_rlcmac_ts_alloc.cpp new file mode 100644 index 00000000..366732c2 --- /dev/null +++ b/src/gprs_rlcmac_ts_alloc.cpp @@ -0,0 +1,681 @@ +/* gprs_rlcmac.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <gprs_rlcmac.h> +#include <gprs_debug.h> +#include <bts.h> +#include <tbf.h> + +#include <errno.h> + +/* 3GPP TS 05.02 Annex B.1 */ + +#define MS_NA 255 /* N/A */ +#define MS_A 254 /* 1 with hopping, 0 without */ +#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/ +#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/ + +struct gprs_ms_multislot_class { + uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */ + uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */ + uint8_t type; /* Type of Mobile */ +}; + +static const struct gprs_ms_multislot_class gprs_ms_multislot_class[32] = { +/* M-S Class Rx Tx Sum Tta Ttb Tra Trb Type */ +/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, +/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 }, +/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 }, +/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 }, +/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 }, +/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 }, +/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 }, +/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 }, +/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 }, +/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 }, +/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 }, +/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 }, +/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 }, +/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, +/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, +/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 }, +/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 }, +/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 }, +/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 }, +/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 }, +/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 }, +/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 }, +/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 }, +/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 }, +/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 }, +/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, +/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA }, +}; + +static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch) +{ + struct gprs_rlcmac_tbf *tbf; + uint8_t usf_map = 0; + uint8_t tfi, usf; + + /* make map of used USF */ + for (tfi = 0; tfi < 32; tfi++) { + tbf = pdch->ul_tbf[tfi]; + if (!tbf) + continue; + usf_map |= (1 << tbf->dir.ul.usf[pdch->ts_no]); + } + + /* look for USF, don't use USF=7 */ + for (usf = 0; usf < 7; usf++) { + if (!(usf_map & (1 << usf))) + return usf; + } + + return -1; +} + +static int find_enabled_pdch(struct gprs_rlcmac_trx *trx, const uint8_t start_ts) +{ + int ts; + for (ts = start_ts; ts < 8; ts++) { + struct gprs_rlcmac_pdch *pdch; + + pdch = &trx->pdch[ts]; + if (!pdch->is_enabled()) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because " + "not enabled\n", ts); + continue; + } + return ts; + } + + return 8; +} + +static void assign_uplink_tbf_usf( + struct gprs_rlcmac_pdch *pdch, + struct gprs_rlcmac_tbf *tbf, int8_t usf) +{ + tbf->trx->ul_tbf[tbf->tfi()] = tbf; + pdch->ul_tbf[tbf->tfi()] = tbf; + tbf->pdch[pdch->ts_no] = pdch; + tbf->dir.ul.usf[pdch->ts_no] = usf; +} + +static void assign_dlink_tbf( + struct gprs_rlcmac_pdch *pdch, + struct gprs_rlcmac_tbf *tbf) +{ + tbf->trx->dl_tbf[tbf->tfi()] = tbf; + pdch->dl_tbf[tbf->tfi()] = tbf; + tbf->pdch[pdch->ts_no] = pdch; +} + + +/* Slot Allocation: Algorithm A + * + * Assign single slot for uplink and downlink + */ +int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, + struct gprs_rlcmac_tbf *old_tbf, + struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single) +{ + struct gprs_rlcmac_pdch *pdch; + uint8_t ts; + + LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class " + "%d\n", tbf->ms_class); + + ts = find_enabled_pdch(tbf->trx, 0); + if (ts == 8) + return -EINVAL; + + pdch = &tbf->trx->pdch[ts]; + if (tbf->direction == GPRS_RLCMAC_UL_TBF) { + int8_t usf; /* must be signed */ + + /* if USF available */ + usf = find_free_usf(pdch); + if (usf < 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "- Failed " + "allocating TS=%d, no USF available\n", ts); + return -EBUSY; + } + LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink " + "TS=%d USF=%d\n", ts, usf); + assign_uplink_tbf_usf(pdch, tbf, usf); + } else { + LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d\n", ts); + assign_dlink_tbf(pdch, tbf); + } + /* the only one TS is the common TS */ + tbf->first_ts = tbf->first_common_ts = ts; + + return 0; +} + +/* + * Select a window of Rx slots if available. + * The maximum allowed slots depend on RX or the window of available + * slots. This must be done for uplink TBF also, because it is the basis + * for calculating control slot and uplink slot(s). + */ +static uint8_t select_dl_slots(struct gprs_rlcmac_trx *trx, + const int ms_type, const int ms_max_rxslots, + uint8_t *out_rx_win_min, uint8_t *out_rx_win_max) + +{ + uint8_t rx_window = 0; + int rx_window_size = 0; + int8_t last_tsc = -1; /* must be signed */ + uint8_t rx_win_min = 0, rx_win_max = 0; + + for (int ts_no = 0; ts_no < 8; ts_no++) { + struct gprs_rlcmac_pdch *pdch; + pdch = &trx->pdch[ts_no]; + + /* check if enabled */ + if (!pdch->is_enabled()) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because " + "not enabled\n", ts_no); + if (ms_type == 1 && rx_window) + goto inc_window; + continue; + } + /* check if TSC changes */ + if (last_tsc < 0) + last_tsc = pdch->tsc; + else if (last_tsc != pdch->tsc) { + LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of TRX=%d, " + "because it has different TSC than lower TS " + "of TRX. In order to allow multislot, all " + "slots must be configured with the same " + "TSC!\n", ts_no, trx->trx_no); + if (ms_type == 1 && rx_window) + goto inc_window; + continue; + } + + if (!rx_window) + rx_win_min = ts_no; + + rx_window |= (1 << ts_no); + LOGP(DRLCMAC, LOGL_DEBUG, "- Selected DL TS %d\n", ts_no); + + /* range of window (required for Type 1) */ + rx_win_max = ts_no; + +inc_window: + if (++rx_window_size == ms_max_rxslots) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / " + "window reached maximum alowed Rx size\n"); + break; + } + if (ms_type == 1 && rx_window_size == 5) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / " + "window reached maximum supported Rx size of " + "this algorithm\n"); + break; + } + } + + LOGP(DRLCMAC, LOGL_DEBUG, "- Selected slots for RX: " + "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", + ((rx_window & 0x01)) ? 'D' : '.', + ((rx_window & 0x02)) ? 'D' : '.', + ((rx_window & 0x04)) ? 'D' : '.', + ((rx_window & 0x08)) ? 'D' : '.', + ((rx_window & 0x10)) ? 'D' : '.', + ((rx_window & 0x20)) ? 'D' : '.', + ((rx_window & 0x40)) ? 'D' : '.', + ((rx_window & 0x80)) ? 'D' : '.'); + + *out_rx_win_min = rx_win_min; + *out_rx_win_max = rx_win_max; + return rx_window; +} + +static int reduce_rx_window(const int ms_type, const struct gprs_rlcmac_tbf *old_tbf, + const int Tt, const int Tr, + int *rx_window, + uint8_t *rx_win_min, uint8_t *rx_win_max) +{ + if (ms_type != 1) + return 0; + if (!old_tbf) + return 0; + if (old_tbf->direction != GPRS_RLCMAC_UL_TBF) + return 0; + + uint8_t collide = 0, ul_usage = 0; + + /* calculate mask of colliding slots */ + for (uint8_t ts_no = 0; ts_no < 8; ts_no++) { + int j; + if (!old_tbf->pdch[ts_no]) + continue; + + ul_usage |= (1 << ts_no); + /* mark bits from TS-t .. TS+r */ + for (j = ts_no - Tt; j != ((ts_no + Tr + 1) & 7); j = (j + 1) & 7) + collide |= (1 << j); + } + + LOGP(DRLCMAC, LOGL_DEBUG, "- Not allowed slots due to existing " + "UL allocation: (TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7) " + " D=downlink x=not usable\n", + ((ul_usage & 0x01)) ? 'D' : ((collide & 0x01))?'x':'.', + ((ul_usage & 0x02)) ? 'D' : ((collide & 0x02))?'x':'.', + ((ul_usage & 0x04)) ? 'D' : ((collide & 0x04))?'x':'.', + ((ul_usage & 0x08)) ? 'D' : ((collide & 0x08))?'x':'.', + ((ul_usage & 0x10)) ? 'D' : ((collide & 0x10))?'x':'.', + ((ul_usage & 0x20)) ? 'D' : ((collide & 0x20))?'x':'.', + ((ul_usage & 0x40)) ? 'D' : ((collide & 0x40))?'x':'.', + ((ul_usage & 0x80)) ? 'D' : ((collide & 0x80))?'x':'.'); + + /* + * Uplink/Downlink in GSM is shifted by three timeslots. Make + * sure they don't collide. + */ + *rx_window &= ~(collide << 3); + *rx_window &= ~(collide >> 5); + LOGP(DRLCMAC, LOGL_DEBUG, "- Remaining slots for RX: " + "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", + ((*rx_window & 0x01)) ? 'D' : '.', + ((*rx_window & 0x02)) ? 'D' : '.', + ((*rx_window & 0x04)) ? 'D' : '.', + ((*rx_window & 0x08)) ? 'D' : '.', + ((*rx_window & 0x10)) ? 'D' : '.', + ((*rx_window & 0x20)) ? 'D' : '.', + ((*rx_window & 0x40)) ? 'D' : '.', + ((*rx_window & 0x80)) ? 'D' : '.'); + + if (!*rx_window) { + LOGP(DRLCMAC, LOGL_NOTICE, "No suitable downlink slots " + "available with current uplink assignment\n"); + return -EBUSY; + } + + return 0; +} + +/* shrink range of rx_win_min and rx_win_max */ +static void shrink_rx_window(uint8_t *rx_win_min, uint8_t *rx_win_max, int rx_window) +{ + /* calculate new min/max */ + for (uint8_t ts_no = *rx_win_min; ts_no <= *rx_win_max; ts_no++) { + if ((rx_window & (1 << ts_no))) + break; + *rx_win_min = ts_no + 1; + LOGP(DRLCMAC, LOGL_DEBUG, "- TS is unused, so " + "raising start of DL window to %d\n", + *rx_win_min); + } + for (uint8_t ts_no = *rx_win_max; ts_no >= *rx_win_min; ts_no--) { + if ((rx_window & (1 << ts_no))) + break; + *rx_win_max = ts_no - 1; + LOGP(DRLCMAC, LOGL_DEBUG, "- TS is unused, so " + "lowering end of DL window to %d\n", + *rx_win_max); + } +} + +/* + * reduce window, to allow at least one uplink TX slot + * this is only required for Type 1 + */ +static uint8_t update_rx_win_max(const int ms_type, const int Tt, + const int Tr, uint8_t rx_win_min, uint8_t rx_win_max) +{ + if (ms_type != 1) + return rx_win_max; + + if (rx_win_max - rx_win_min + 1 + Tt + 1 + Tr > 8) { + rx_win_max = rx_win_min + 7 - Tt - 1 - Tr; + LOGP(DRLCMAC, LOGL_DEBUG, "- Reduce RX window due to time " + "contraints to %d slots\n", rx_win_max - rx_win_min + 1); + } + + return rx_win_max; +} + +static void tx_win_from_rx(const int ms_type, + uint8_t rx_win_min, uint8_t rx_win_max, + int Tt, int Tr, + uint8_t *tx_win_min, uint8_t *tx_win_max, + uint8_t *tx_range) +{ + if (ms_type == 1) { + /* calculate TX window (shifted by 3 timeslots) + * it uses the space between tx_win_max and tx_win_min */ + *tx_win_min = (rx_win_max - 2 + Tt) & 7; + *tx_win_max = (rx_win_min + 4 - Tr) & 7; + } else { + /* TX and RX simultaniously */ + *tx_win_min = rx_win_min; + *tx_win_max = 7; + } + + *tx_range = (*tx_win_max - *tx_win_min + 1) & 7; + /* if TX window fills complete range */ + if (*tx_range == 0) + *tx_range = 8; + LOGP(DRLCMAC, LOGL_DEBUG, "- TX-Window is: %d..%d\n", *tx_win_min, + *tx_win_max); +} + +/* + * Select a window of Tx slots if available. + * The maximum allowed slots depend on TX or the window of available + * slots. + */ +static int select_ul_slots(gprs_rlcmac_trx *trx, + const int ms_type, const int ms_max_txslots, + uint8_t tx_win_min, uint8_t tx_range, + int8_t *usf, int8_t *first_common_ts, uint8_t rx_window) +{ + int tsc = -1; + uint8_t tx_window = 0; + int i; + uint8_t ts_no; + + for (ts_no = tx_win_min, i = 0; i < tx_range; ts_no = (ts_no + 1) & 7, i++) { + gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no]; + + /* check if enabled */ + if (!pdch->is_enabled()) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " + "because not enabled\n", ts_no); + if (ms_type == 1 && tx_window) + goto inc_window; + continue; + } + /* check if used as downlink */ + if (!(rx_window & (1 << ts_no))) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " + "because not a downlink slot\n", ts_no); + if (ms_type == 1 && tx_window) + goto inc_window; + continue; + } + /* check if TSC changes */ + if (tsc < 0) + tsc = pdch->tsc; + else if (tsc != pdch->tsc) { + LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of " + "TRX=%d, because it has different TSC " + "than lower TS of TRX. In order to " + "allow multislot, all slots must be " + "configured with the same TSC!\n", + ts_no, trx->trx_no); + if (ms_type == 1) + goto inc_window; + continue; + } + /* check for free usf */ + usf[ts_no] = find_free_usf(pdch); + if (usf[ts_no] < 0) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " + "because no USF available\n", ts_no); + if (ms_type == 1) + goto inc_window; + continue; + } + + if (!tx_window) + *first_common_ts = ts_no; + + tx_window |= (1 << ts_no); + LOGP(DRLCMAC, LOGL_DEBUG, "- Selected UL TS %d\n", ts_no); + +inc_window: + if (1 && ms_type == 1) { /* FIXME: multislot UL assignment */ + LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because " + "1 slot assigned\n"); + break; + } + if (i+1 == ms_max_txslots) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because " + "slots / window reached maximum " + "allowed Tx size\n"); + break; + } + } + + LOGP(DRLCMAC, LOGL_DEBUG, "- Selected TX window: " + "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n", + ((tx_window & 0x01)) ? 'U' : '.', + ((tx_window & 0x02)) ? 'U' : '.', + ((tx_window & 0x04)) ? 'U' : '.', + ((tx_window & 0x08)) ? 'U' : '.', + ((tx_window & 0x10)) ? 'U' : '.', + ((tx_window & 0x20)) ? 'U' : '.', + ((tx_window & 0x40)) ? 'U' : '.', + ((tx_window & 0x80)) ? 'U' : '.'); + + if (!tx_window) { + LOGP(DRLCMAC, LOGL_NOTICE, "No suitable uplink slots " + "available\n"); + return -EBUSY; + } + + return tx_window; +} + +/* + * Assign the first common ts, which is used for control or + * single slot. + */ +static int select_first_ts(gprs_rlcmac_trx *trx, uint8_t tx_win_min, + uint8_t tx_range, uint8_t rx_window) +{ + uint8_t ts_no; + int i; + for (ts_no = tx_win_min, i = 0; i < tx_range; ts_no = (ts_no + 1) & 7, i++) { + gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no]; + /* check if enabled */ + if (!pdch->is_enabled()) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " + "because not enabled\n", ts_no); + continue; + } + /* check if used as downlink */ + if (!(rx_window & (1 << ts_no))) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, " + "because not a downlink slot\n", ts_no); + continue; + } + return ts_no; + } + + return -1; +} + +/* Slot Allocation: Algorithm B + * + * Assign as many downlink slots as possible. + * Assign one uplink slot. (With free USF) + * + */ +int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, + struct gprs_rlcmac_tbf *old_tbf, + struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single) +{ + const struct gprs_ms_multislot_class *ms_class; + uint8_t Tx, Sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */ + uint8_t Tta, Ttb, Tra, Trb, Tt, Tr; /* Minimum Number of Slots */ + uint8_t Type; /* Type of Mobile */ + int rx_window; + static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" }; + int8_t usf[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; /* must be signed */ + int8_t first_common_ts = -1; + uint8_t ts; + uint8_t slotcount = 0; + + + if (tbf->ms_class >= 32) { + LOGP(DRLCMAC, LOGL_ERROR, "Multislot class %d out of range.\n", + tbf->ms_class); + return -EINVAL; + } + + if (tbf->ms_class) { + ms_class = &gprs_ms_multislot_class[tbf->ms_class]; + LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for " + "class %d\n", tbf->ms_class); + } else { + ms_class = &gprs_ms_multislot_class[12]; + LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for " + "unknow class (assuming 12)\n"); + } + + if (ms_class->tx == MS_NA) { + LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not " + "applicable.\n", tbf->ms_class); + return -EINVAL; + } + + Tx = ms_class->tx; + Sum = ms_class->sum; + Tta = ms_class->ta; + Ttb = ms_class->tb; + Tra = ms_class->ra; + Trb = ms_class->rb; + Type = ms_class->type; + + /* Tta and Ttb may depend on hopping or frequency change */ + if (Ttb == MS_A || Ttb == MS_B) + Ttb = 0; + if (Trb == MS_A || Trb == MS_C) + Trb = 0; + + LOGP(DRLCMAC, LOGL_DEBUG, "- Rx=%d Tx=%d Sum Rx+Tx=%s Tta=%s Ttb=%d " + " Tra=%d Trb=%d Type=%d\n", ms_class->rx, Tx, + (Sum == MS_NA) ? "N/A" : digit[Sum], + (Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type); + + /* select the values for time contraints */ + /* applicable to type 1 and type 2 */ + Tt = Ttb; + Tr = Trb; + + uint8_t rx_win_min, rx_win_max; + rx_window = select_dl_slots(tbf->trx, ms_class->type, ms_class->rx, + &rx_win_min, &rx_win_max); + + + /* reduce window, if existing uplink slots collide RX window */ + int rc = reduce_rx_window(ms_class->type, old_tbf, Tt, Tr, + &rx_window, &rx_win_min, &rx_win_max); + if (rc < 0) + return rc; + shrink_rx_window(&rx_win_min, &rx_win_max, rx_window); + rx_win_max = update_rx_win_max(ms_class->type, Tt, Tr, + rx_win_min, rx_win_max); + shrink_rx_window(&rx_win_min, &rx_win_max, rx_window); + LOGP(DRLCMAC, LOGL_DEBUG, "- RX-Window is: %d..%d\n", rx_win_min, + rx_win_max); + + /* calculate TX window */ + uint8_t tx_win_min, tx_win_max, tx_range; + tx_win_from_rx(ms_class->type, rx_win_min, rx_win_max, Tt, Tr, + &tx_win_min, &tx_win_max, &tx_range); + + /* select UL slots but in both cases assign first_common_ts */ + uint8_t tx_window = 0; + if (tbf->direction == GPRS_RLCMAC_UL_TBF) { + rc = select_ul_slots(tbf->trx, ms_class->type, ms_class->tx, + tx_win_min, tx_range, usf, + &first_common_ts, rx_window); + if (rc < 0) + return rc; + tx_window = rc; + } else { + first_common_ts = select_first_ts(tbf->trx, tx_win_min, + tx_range, rx_window); + } + #warning "first_common_ts might be different if there was no free USF for the new uplink assignment" + + if (first_common_ts < 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n"); + return -EINVAL; + } + + if (tbf->direction == GPRS_RLCMAC_DL_TBF) { + /* assign downlink */ + if (rx_window == 0) { + LOGP(DRLCMAC, LOGL_NOTICE, "No downlink slots " + "available\n"); + return -EINVAL; + } + for (ts = 0; ts < 8; ts++) { + if ((rx_window & (1 << ts))) { + /* be sure to select a single downlink slots + * that can be used for uplink, if multiple + * slots are assigned later. */ + if (single && first_common_ts != ts) + continue; + LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS " + "%d\n", ts); + assign_dlink_tbf(&tbf->trx->pdch[ts], tbf); + slotcount++; + if (slotcount == 1) + tbf->first_ts = ts; + if (single) + break; + } + } + } else { + for (ts = 0; ts < 8; ts++) { + if ((tx_window & (1 << ts))) { + LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS " + "%d\n", ts); + assign_uplink_tbf_usf(&tbf->trx->pdch[ts], tbf, usf[ts]); + slotcount++; + if (slotcount == 1) + tbf->first_ts = ts; + if (single) + break; + } + } + } + if (single && slotcount) { + LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n", + tbf->first_ts, + (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL"); + } else { + LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount, + (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL"); + } + if (slotcount == 0) + return -EBUSY; + + tbf->first_common_ts = first_common_ts; + + return 0; +} diff --git a/src/gsm_timer.cpp b/src/gsm_timer.cpp index 95513b47..d3c59cbc 100644 --- a/src/gsm_timer.cpp +++ b/src/gsm_timer.cpp @@ -33,9 +33,21 @@ static int *nearest_p; #include <limits.h> #include <gsm_timer.h> #include <pcu_l1_if.h> +#include <bts.h> + static struct rb_root timer_root = RB_ROOT; +/* + * TODO: make this depend on the BTS. This means that + * all time functions schedule based on the BTS they + * are scheduled on. + */ +static int get_current_fn() +{ + return BTS::main_bts()->current_frame_number(); +} + static void __add_gsm_timer(struct osmo_gsm_timer_list *timer) { struct rb_node **new_node = &(timer_root.rb_node); diff --git a/src/llc.cpp b/src/llc.cpp new file mode 100644 index 00000000..2d295f86 --- /dev/null +++ b/src/llc.cpp @@ -0,0 +1,142 @@ +/* Copied from tbf.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <tbf.h> +#include <bts.h> + +#include <stdio.h> + +extern "C" { +#include <osmocom/core/msgb.h> +} + +/* reset LLC frame */ +void gprs_llc::reset() +{ + m_index = 0; + m_length = 0; + + memset(frame, 0x42, sizeof(frame)); +} + +void gprs_llc::reset_frame_space() +{ + m_index = 0; +} + +void gprs_llc::enqueue(struct msgb *llc_msg) +{ + m_queue_size += 1; + msgb_enqueue(&queue, llc_msg); +} + +void gprs_llc::put_frame(const uint8_t *data, size_t len) +{ + /* only put frames when we are empty */ + OSMO_ASSERT(m_index == 0 && m_length == 0); + append_frame(data, len); +} + +void gprs_llc::append_frame(const uint8_t *data, size_t len) +{ + /* TODO: bounds check */ + memcpy(frame + m_length, data, len); + m_length += len; +} + +void gprs_llc::clear(BTS *bts) +{ + struct msgb *msg; + + while ((msg = msgb_dequeue(&queue))) { + bts->llc_dropped_frame(); + msgb_free(msg); + } + + m_queue_size = 0; +} + +void gprs_llc::init() +{ + INIT_LLIST_HEAD(&queue); + m_queue_size = 0; + m_avg_queue_delay = 0; + reset(); +} + +#define ALPHA 0.5f + +struct msgb *gprs_llc::dequeue() +{ + struct msgb *msg; + struct timeval *tv, tv_now, tv_result; + uint32_t lifetime; + + + msg = msgb_dequeue(&queue); + if (!msg) + return NULL; + + m_queue_size -= 1; + + /* take the second time */ + gettimeofday(&tv_now, NULL); + tv = (struct timeval *)&msg->data[sizeof(*tv)]; + timersub(&tv_now, tv, &tv_result); + + lifetime = tv_result.tv_sec*1000 + tv_result.tv_usec/1000; + m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA); + + return msg; +} + + +void gprs_llc::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv) +{ + uint16_t delay_csec; + if (bts->bts_data()->force_llc_lifetime) + delay_csec = bts->bts_data()->force_llc_lifetime; + else + delay_csec = pdu_delay_csec; + + /* keep timestamp at 0 for infinite delay */ + if (delay_csec == 0xffff) { + memset(tv, 0, sizeof(*tv)); + return; + } + + /* calculate timestamp of timeout */ + struct timeval now, csec; + gettimeofday(&now, NULL); + csec.tv_usec = (delay_csec % 100) * 10000; + csec.tv_sec = delay_csec / 100; + + timeradd(&now, &csec, tv); +} + +bool gprs_llc::is_frame_expired(struct timeval *tv_now, struct timeval *tv) +{ + /* Timeout is infinite */ + if (tv->tv_sec == 0 && tv->tv_usec == 0) + return false; + + return timercmp(tv_now, tv, >); +} diff --git a/src/llc.h b/src/llc.h new file mode 100644 index 00000000..1f0c1146 --- /dev/null +++ b/src/llc.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include <stdint.h> +#include <string.h> + +#define LLC_MAX_LEN 1543 + +/** + * I represent the LLC data to a MS + */ +struct gprs_llc { + static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv); + static bool is_frame_expired(struct timeval *now, struct timeval *tv); + + void init(); + void reset(); + void reset_frame_space(); + + void enqueue(struct msgb *llc_msg); + struct msgb *dequeue(); + + void put_frame(const uint8_t *data, size_t len); + void append_frame(const uint8_t *data, size_t len); + + void consume(size_t len); + void consume(uint8_t *data, size_t len); + void clear(BTS *bts); + + uint16_t chunk_size() const; + uint16_t remaining_space() const; + uint16_t frame_length() const; + + bool fits_in_current_frame(uint8_t size) const; + + uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */ + uint16_t m_index; /* current write/read position of frame */ + uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */ + struct llist_head queue; /* queued LLC DL data */ + + uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */ + size_t m_queue_size; +}; + +inline uint16_t gprs_llc::chunk_size() const +{ + return m_length - m_index; +} + +inline uint16_t gprs_llc::remaining_space() const +{ + return LLC_MAX_LEN - m_length; +} + +inline uint16_t gprs_llc::frame_length() const +{ + return m_length; +} + +inline void gprs_llc::consume(size_t len) +{ + m_index += len; +} + +inline void gprs_llc::consume(uint8_t *data, size_t len) +{ + /* copy and increment index */ + memcpy(data, frame + m_index, len); + consume(len); +} + +inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const +{ + return m_length + chunk_size <= LLC_MAX_LEN; +} diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp index a31974f5..ce13b96f 100644 --- a/src/pcu_l1_if.cpp +++ b/src/pcu_l1_if.cpp @@ -37,6 +37,7 @@ extern "C" { #include <gprs_debug.h> #include <gprs_bssgp_pcu.h> #include <pcuif_proto.h> +#include <bts.h> #include <tbf.h> // FIXME: move this, when changed from c++ to c. @@ -49,19 +50,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn, extern void *tall_pcu_ctx; -// Variable for storage current FN. -int frame_number; - -int get_current_fn() -{ - return frame_number; -} - -void set_current_fn(int fn) -{ - frame_number = fn; -} - /* * PCU messages */ @@ -137,7 +125,7 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr) { #ifdef ENABLE_SYSMODSP - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); if (bts->trx[trx].fl1h) l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr, @@ -153,7 +141,7 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr) { #ifdef ENABLE_SYSMODSP - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); if (bts->trx[trx].fl1h) l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr, @@ -192,10 +180,13 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi) pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3); } -extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data, +extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi) { - return gprs_rlcmac_rcv_block(trx, ts, data, len, fn, rssi); + struct gprs_rlcmac_pdch *pdch; + + pdch = &bts_main_data()->trx[trx_no].pdch[ts_no]; + return pdch->rcv_block(data, len, fn, rssi); } static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind) @@ -232,8 +223,7 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf) switch (data_cnf->sapi) { case PCU_IF_SAPI_PCH: if (data_cnf->data[2] == 0x3f) - rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data, - data_cnf->fn); + BTS::main_bts()->rcv_imm_ass_cnf(data_cnf->data, data_cnf->fn); break; default: LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with " @@ -248,7 +238,8 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf) extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr) { - return gprs_rlcmac_rcv_rts_block(trx, ts, arfcn, fn, block_nr); + return gprs_rlcmac_rcv_rts_block(bts_main_data(), + trx, ts, arfcn, fn, block_nr); } static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req) @@ -292,7 +283,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind) switch (rach_ind->sapi) { case PCU_IF_SAPI_RACH: - rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn, + rc = BTS::main_bts()->rcv_rach( + rach_ind->ra, rach_ind->fn, rach_ind->qta); break; default: @@ -304,31 +296,9 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind) return rc; } -int flush_pdch(struct gprs_rlcmac_pdch *pdch, uint8_t trx, uint8_t ts) -{ - struct gprs_rlcmac_paging *pag; - struct gprs_rlcmac_sba *sba, *sba2; - - /* kick all TBF on slot */ - gprs_rlcmac_tbf::free_all(pdch); - - /* flush all pending paging messages */ - while ((pag = gprs_rlcmac_dequeue_paging(pdch))) - talloc_free(pag); - - llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) { - if (sba->trx == trx && sba->ts == ts) { - llist_del(&sba->list); - talloc_free(sba); - } - } - - return 0; -} - static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); struct gprs_bssgp_pcu *pcu; struct gprs_rlcmac_pdch *pdch; struct in_addr ia; @@ -351,11 +321,8 @@ bssgp_failed: /* free all TBF */ for (trx = 0; trx < 8; trx++) { bts->trx[trx].arfcn = info_ind->trx[trx].arfcn; - for (ts = 0; ts < 8; ts++) { - if (bts->trx[trx].pdch[ts].enable) - flush_pdch(&bts->trx[trx].pdch[ts], - trx, ts); - } + for (ts = 0; ts < 8; ts++) + bts->trx[trx].pdch[ts].free_resources(); } gprs_bssgp_destroy_or_exit(); return 0; @@ -474,7 +441,7 @@ bssgp_failed: pdch = &bts->trx[trx].pdch[ts]; if ((info_ind->trx[trx].pdch_mask & (1 << ts))) { /* FIXME: activate dynamically at RLCMAC */ - if (!pdch->enable) { + if (!pdch->is_enabled()) { #ifdef ENABLE_SYSMODSP if ((info_ind->flags & PCU_IF_FLAG_SYSMO)) @@ -482,17 +449,16 @@ bssgp_failed: bts->trx[trx].fl1h, ts); #endif pcu_tx_act_req(trx, ts, 1); - INIT_LLIST_HEAD(&pdch->paging_list); - pdch->enable = 1; + pdch->enable(); } pdch->tsc = info_ind->trx[trx].tsc[ts]; LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n", trx, ts); } else { - if (pdch->enable) { + if (pdch->is_enabled()) { pcu_tx_act_req(trx, ts, 0); - pdch->enable = 0; - flush_pdch(pdch, trx, ts); + pdch->free_resources(); + pdch->disable(); } } } @@ -503,9 +469,6 @@ bssgp_failed: static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind) { - struct gprs_rlcmac_tbf *tbf; - struct gprs_rlcmac_sba *sba, *sba2; - uint32_t elapsed; uint8_t fn13 = time_ind->fn % 13; /* omit frame numbers not starting at a MAC block */ @@ -515,33 +478,7 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind) // LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", // time_ind->fn % 52); - set_current_fn(time_ind->fn); - - /* check for poll timeout */ - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { - if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) { - elapsed = (frame_number + 2715648 - tbf->poll_fn) - % 2715648; - if (elapsed >= 20 && elapsed < 2715400) - gprs_rlcmac_poll_timeout(tbf); - } - } - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { - if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) { - elapsed = (frame_number + 2715648 - tbf->poll_fn) - % 2715648; - if (elapsed >= 20 && elapsed < 2715400) - gprs_rlcmac_poll_timeout(tbf); - } - } - llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) { - elapsed = (frame_number + 2715648 - sba->fn) % 2715648; - if (elapsed >= 20 && elapsed < 2715400) { - /* sba will be freed here */ - gprs_rlcmac_sba_timeout(sba); - } - } - + BTS::main_bts()->set_current_frame_number(time_ind->fn); return 0; } @@ -550,7 +487,7 @@ static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req) LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d " "length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]); - return gprs_rlcmac_add_paging(pag_req->chan_needed, + return BTS::main_bts()->add_paging(pag_req->chan_needed, pag_req->identity_lv); } diff --git a/src/pcu_l1_if.h b/src/pcu_l1_if.h index f01b95ad..bd8fe940 100644 --- a/src/pcu_l1_if.h +++ b/src/pcu_l1_if.h @@ -32,8 +32,6 @@ extern "C" { #ifdef __cplusplus } -int get_current_fn(); - void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, uint32_t fn, uint8_t block_nr); void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp index 754043f9..c0dac142 100644 --- a/src/pcu_main.cpp +++ b/src/pcu_main.cpp @@ -27,27 +27,23 @@ #include <getopt.h> #include <signal.h> #include <sched.h> +#include <bts.h> extern "C" { #include "pcu_vty.h" #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> } -struct gprs_rlcmac_bts *gprs_rlcmac_bts; extern struct gprs_nsvc *nsvc; uint16_t spoof_mcc = 0, spoof_mnc = 0; static int config_given = 0; -static const char *config_file = "osmo-pcu.cfg"; +static char *config_file = strdup("osmo-pcu.cfg"); extern struct vty_app_info pcu_vty_info; void *tall_pcu_ctx; extern void *bv_tall_ctx; static int quit = 0; static int rt_prio = -1; -#ifdef DEBUG_DIAGRAM -extern struct timeval diagram_time; -#endif - static void print_help() { printf( "Some useful options:\n" @@ -91,6 +87,7 @@ static void handle_options(int argc, char **argv) exit(0); break; case 'c': + free(config_file); config_file = strdup(optarg); config_given = 1; break; @@ -163,10 +160,7 @@ int main(int argc, char *argv[]) return -ENOMEM; bv_tall_ctx = tall_pcu_ctx; - bts = gprs_rlcmac_bts = talloc_zero(tall_pcu_ctx, - struct gprs_rlcmac_bts); - if (!gprs_rlcmac_bts) - return -ENOMEM; + bts = bts_main_data(); bts->fc_interval = 1; bts->initial_cs_dl = bts->initial_cs_ul = 1; bts->cs1 = 1; @@ -244,18 +238,13 @@ int main(int argc, char *argv[]) osmo_gsm_timers_update(); osmo_select_main(0); -#ifdef DEBUG_DIAGRAM - gettimeofday(&diagram_time, NULL); -#endif } telnet_exit(); pcu_l1if_close(); - flush_timing_advance(); - - talloc_free(gprs_rlcmac_bts); + bts->bts->timing_advance()->flush(); talloc_report_full(tall_pcu_ctx, stderr); talloc_free(tall_pcu_ctx); diff --git a/src/pcu_vty.c b/src/pcu_vty.c index 3ab6865c..041d5294 100644 --- a/src/pcu_vty.c +++ b/src/pcu_vty.c @@ -6,6 +6,7 @@ #include <osmocom/core/linuxlist.h> #include "pcu_vty.h" #include "gprs_rlcmac.h" +#include "bts.h" enum node_type pcu_vty_go_parent(struct vty *vty) { @@ -76,7 +77,7 @@ gDEFUN(ournode_end, ournode_end_cmd, "end", static int config_write_pcu(struct vty *vty) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); vty_out(vty, "pcu%s", VTY_NEWLINE); vty_out(vty, " flow-control-interval %d%s", bts->fc_interval, @@ -102,6 +103,7 @@ static int config_write_pcu(struct vty *vty) vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE); vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE); + return CMD_SUCCESS; } /* per-BTS configuration */ @@ -121,7 +123,7 @@ DEFUN(cfg_pcu_fc_interval, "Interval between sending subsequent Flow Control PDUs\n" "Interval time in seconds\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->fc_interval = atoi(argv[0]); @@ -134,7 +136,7 @@ DEFUN(cfg_pcu_cs, "Set the Coding Scheme to be used, (overrides BTS config)\n" "Initial CS used\nAlternative uplink CS") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); uint8_t cs = atoi(argv[0]); bts->force_cs = 1; @@ -152,7 +154,7 @@ DEFUN(cfg_pcu_no_cs, "no cs", NO_STR "Don't force given Coding Scheme, (use BTS config)\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->force_cs = 0; @@ -168,7 +170,7 @@ DEFUN(cfg_pcu_queue_lifetime, "queue lifetime <1-65534>", QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); uint8_t csec = atoi(argv[0]); bts->force_llc_lifetime = csec; @@ -181,7 +183,7 @@ DEFUN(cfg_pcu_queue_lifetime_inf, "queue lifetime infinite", QUEUE_STR LIFETIME_STR "Infinite lifetime") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->force_llc_lifetime = 0xffff; @@ -194,7 +196,7 @@ DEFUN(cfg_pcu_no_queue_lifetime, NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given " "by SGSN)\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->force_llc_lifetime = 0; @@ -208,7 +210,7 @@ DEFUN(cfg_pcu_alloc, "PACCH\nSingle slot is assigned only\nMultiple slots are assigned for " "semi-duplex operation") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); switch (argv[0][0]) { case 'a': @@ -227,7 +229,7 @@ DEFUN(cfg_pcu_two_phase, "two-phase-access", "Force two phase access when MS requests single phase access\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->force_two_phase = 1; @@ -239,7 +241,7 @@ DEFUN(cfg_pcu_no_two_phase, "no two-phase-access", NO_STR "Only use two phase access when requested my MS\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->force_two_phase = 0; @@ -253,7 +255,7 @@ DEFUN(cfg_pcu_alpha, "NOTE: Be sure to set Alpha value at System information 13 too.\n" "Alpha in units of 0.1\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->alpha = atoi(argv[0]); @@ -266,13 +268,43 @@ DEFUN(cfg_pcu_gamma, "Gamma parameter for MS power control in units of dB (see TS 05.08)\n" "Gamma in even unit of dBs\n") { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); bts->gamma = atoi(argv[0]) / 2; return CMD_SUCCESS; } +DEFUN(show_bts_stats, + show_bts_stats_cmd, + "show bts statistics", + SHOW_STR "BTS related functionality\nStatistics\n") +{ + vty_out_rate_ctr_group(vty, "", bts_main_data_stats()); + return CMD_SUCCESS; +} + +DEFUN(show_tbf, + show_tbf_cmd, + "show tbf all", + SHOW_STR "information about all current TBFs\n") +{ + struct gprs_rlcmac_bts *bts = bts_main_data(); + struct llist_head *tbf; + + vty_out(vty, "UL TBFs%s", VTY_NEWLINE); + llist_for_each(tbf, &bts->ul_tbfs) { + tbf_print_vty_info(vty, tbf); + } + + vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE); + llist_for_each(tbf, &bts->dl_tbfs) { + tbf_print_vty_info(vty, tbf); + } + + return CMD_SUCCESS; +} + static const char pcu_copyright[] = "Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n" " Andreas Eversberg <jolly@eversberg.eu>\r\n" @@ -310,5 +342,8 @@ int pcu_vty_init(const struct log_info *cat) install_element(PCU_NODE, &cfg_pcu_gamma_cmd); install_element(PCU_NODE, &ournode_end_cmd); + install_element_ve(&show_bts_stats_cmd); + install_element_ve(&show_tbf_cmd); + return 0; } diff --git a/src/poll_controller.cpp b/src/poll_controller.cpp new file mode 100644 index 00000000..0f46472f --- /dev/null +++ b/src/poll_controller.cpp @@ -0,0 +1,63 @@ +/* poll_controller.h + * + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 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 Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <poll_controller.h> +#include <bts.h> +#include <tbf.h> + +PollController::PollController(BTS& bts) + : m_bts(bts) +{} + +void PollController::expireTimedout(int frame_number) +{ + struct gprs_rlcmac_bts *bts = m_bts.bts_data(); + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_sba *sba, *sba2; + uint32_t elapsed; + + /* check for poll timeout */ + llist_for_each_entry(tbf, &bts->ul_tbfs, list) { + if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) { + elapsed = (frame_number + 2715648 - tbf->poll_fn) + % 2715648; + if (elapsed >= 20 && elapsed < 2715400) + tbf->poll_timeout(); + } + } + llist_for_each_entry(tbf, &bts->dl_tbfs, list) { + if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) { + elapsed = (frame_number + 2715648 - tbf->poll_fn) + % 2715648; + if (elapsed >= 20 && elapsed < 2715400) + tbf->poll_timeout(); + } + } + llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) { + elapsed = (frame_number + 2715648 - sba->fn) % 2715648; + if (elapsed >= 20 && elapsed < 2715400) { + /* sba will be freed here */ + m_bts.sba()->timeout(sba); + } + } + +} diff --git a/src/poll_controller.h b/src/poll_controller.h new file mode 100644 index 00000000..67af4084 --- /dev/null +++ b/src/poll_controller.h @@ -0,0 +1,46 @@ +/* poll_controller.h + * + * Copyright (C) 2013 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 Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + +struct gprs_rlcmac_bts; + +class BTS; + +/** + * I belong to a BTS and I am responsible for finding TBFs and + * SBAs that should have been polled and execute the timeout + * action on them. + */ +class PollController { +public: + PollController(BTS& bts); + + void expireTimedout(int frame_number); + +private: + BTS& m_bts; + +private: + /* disable copying to avoid slicing */ + PollController(const PollController&); + PollController& operator=(const PollController&); +}; diff --git a/src/rlc.cpp b/src/rlc.cpp new file mode 100644 index 00000000..36320dc9 --- /dev/null +++ b/src/rlc.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "tbf.h" +#include "bts.h" +#include "gprs_debug.h" + +extern "C" { +#include <osmocom/core/utils.h> +} + + +uint8_t *gprs_rlc_data::prepare(size_t block_data_len) +{ + /* todo.. only set it once if it turns out to be a bottleneck */ + memset(block, 0x0, sizeof(block)); + memset(block, 0x2b, block_data_len); + + return block; +} + +void gprs_rlc_data::put_data(const uint8_t *data, size_t data_len) +{ + memcpy(block, data, data_len); + len = data_len; +} + +void gprs_rlc_v_b::reset() +{ + for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i) + mark_invalid(i); +} + +int gprs_rlc_dl_window::resend_needed() +{ + for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) { + if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn)) + return bsn; + } + + return -1; +} + +int gprs_rlc_dl_window::mark_for_resend() +{ + int resend = 0; + + for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) { + if (m_v_b.is_unacked(bsn)) { + /* mark to be re-send */ + m_v_b.mark_resend(bsn); + resend += 1; + } + } + + return resend; +} + +int gprs_rlc_dl_window::count_unacked() +{ + uint16_t unacked = 0; + uint16_t bsn; + + for (bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) { + if (!m_v_b.is_acked(bsn)) + unacked += 1; + } + + return unacked; +} + +static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns) +{ + return (ssn - 1 - bitnum) & mod_sns; +} + +void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint8_t ssn, + uint16_t *lost, uint16_t *received) +{ + /* SSN - 1 is in range V(A)..V(S)-1 */ + for (int bitpos = 0; bitpos < ws(); bitpos++) { + uint16_t bsn = bitnum_to_bsn(bitpos, ssn, mod_sns()); + + if (bsn == ((v_a() - 1) & mod_sns())) + break; + + if (show_rbb[ws() - 1 - bitpos] == 'R') { + LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn); + if (!m_v_b.is_acked(bsn)) + *received += 1; + m_v_b.mark_acked(bsn); + } else { + LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn); + m_v_b.mark_nacked(bsn); + bts->rlc_nacked(); + *lost += 1; + } + } +} + +int gprs_rlc_dl_window::move_window() +{ + int i; + uint16_t bsn; + int moved = 0; + + for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) { + if (m_v_b.is_acked(bsn)) { + m_v_b.mark_invalid(bsn); + moved += 1; + } else + break; + } + + return moved; +} + +void gprs_rlc_dl_window::show_state(char *show_v_b) +{ + int i; + uint16_t bsn; + + for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) { + uint16_t index = bsn & mod_sns_half(); + switch(m_v_b.get_state(index)) { + case GPRS_RLC_DL_BSN_INVALID: + show_v_b[i] = 'I'; + break; + case GPRS_RLC_DL_BSN_ACKED: + show_v_b[i] = 'A'; + break; + case GPRS_RLC_DL_BSN_RESEND: + show_v_b[i] = 'X'; + break; + case GPRS_RLC_DL_BSN_NACKED: + show_v_b[i] = 'N'; + break; + default: + show_v_b[i] = '?'; + } + } + show_v_b[i] = '\0'; +} + +void gprs_rlc_v_n::reset() +{ + for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i) + m_v_n[i] = GPRS_RLC_UL_BSN_INVALID; +} + +/* Update the receive block bitmap */ +void gprs_rlc_ul_window::update_rbb(char *rbb) +{ + int i; + for (i=0; i < ws(); i++) { + if (m_v_n.is_received(ssn()-1-i)) + rbb[ws()-1-i] = 'R'; + else + rbb[ws()-1-i] = 'I'; + } +} + +/* Raise V(R) to highest received sequence number not received. */ +void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn) +{ + uint16_t offset_v_r; + offset_v_r = (bsn + 1 - v_r()) & mod_sns(); + /* Positive offset, so raise. */ + if (offset_v_r < (sns() >> 1)) { + while (offset_v_r--) { + if (offset_v_r) /* all except the received block */ + m_v_n.mark_missing(v_r()); + raise_v_r_to(1); + } + LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r()); + } +} + +/* + * Raise V(Q) if possible. This is looped until there is a gap + * (non received block) or the window is empty. + */ +uint16_t gprs_rlc_ul_window::raise_v_q() +{ + uint16_t count = 0; + + while (v_q() != v_r()) { + if (!m_v_n.is_received(v_q())) + break; + LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising " + "V(Q) to %d\n", v_q(), (v_q() + 1) & mod_sns()); + raise_v_q(1); + count += 1; + } + + return count; +} + +uint16_t gprs_rlc_ul_window::receive_bsn(const uint16_t bsn) +{ + m_v_n.mark_received(bsn); + raise_v_r(bsn); + + return raise_v_q(); +} diff --git a/src/rlc.h b/src/rlc.h new file mode 100644 index 00000000..17a1360b --- /dev/null +++ b/src/rlc.h @@ -0,0 +1,422 @@ +/* rlc header descriptions + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#pragma once + +#include <stdint.h> + +#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */ +#define RLC_MAX_WS 64 /* max window size */ +#define RLC_MAX_LEN 54 /* CS-4 including spare bits */ + +class BTS; +struct gprs_rlc_v_n; + +/* The state of a BSN in the send/receive window */ +enum gprs_rlc_ul_bsn_state { + GPRS_RLC_UL_BSN_INVALID, + GPRS_RLC_UL_BSN_RECEIVED, + GPRS_RLC_UL_BSN_MISSING, + GPRS_RLC_UL_BSN_MAX, +}; + +enum gprs_rlc_dl_bsn_state { + GPRS_RLC_DL_BSN_INVALID, + GPRS_RLC_DL_BSN_NACKED, + GPRS_RLC_DL_BSN_ACKED, + GPRS_RLC_DL_BSN_UNACKED, + GPRS_RLC_DL_BSN_RESEND, + GPRS_RLC_DL_BSN_MAX, +}; + + +static inline uint16_t mod_sns_half() +{ + return (RLC_MAX_SNS / 2) - 1; +} + +struct gprs_rlc_data { + uint8_t *prepare(size_t block_data_length); + void put_data(const uint8_t *data, size_t len); + + /* block history */ + uint8_t block[RLC_MAX_LEN]; + /* block len of history */ + uint8_t len; +}; + +/* + * I hold the currently transferred blocks and will provide + * the routines to manipulate these arrays. + */ +struct gprs_rlc { + gprs_rlc_data *block(int bsn); + gprs_rlc_data m_blocks[RLC_MAX_SNS/2]; +}; + +/** + * TODO: for GPRS/EDGE maybe make sns a template parameter + * so we create specialized versions... + */ +struct gprs_rlc_v_b { + /* Check for an individual frame */ + bool is_unacked(int bsn) const; + bool is_nacked(int bsn) const; + bool is_acked(int bsn) const; + bool is_resend(int bsn) const; + bool is_invalid(int bsn) const; + gprs_rlc_dl_bsn_state get_state(int bsn) const; + + /* Mark a RLC frame for something */ + void mark_unacked(int bsn); + void mark_nacked(int bsn); + void mark_acked(int bsn); + void mark_resend(int bsn); + void mark_invalid(int bsn); + + void reset(); + + +private: + bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const; + void mark(int bsn, const gprs_rlc_dl_bsn_state state); + + gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */ +}; + + +/** + * TODO: The UL/DL code could/should share a baseclass but + * we are using llist_for_each_entry for the TBF which + * requires everything which creates a requirement for a POD + * type and in < C++11 something that is using even if the + * most simple form of inheritance is not a POD anymore. + */ +struct gprs_rlc_dl_window { + const uint16_t mod_sns() const; + const uint16_t sns() const; + const uint16_t ws() const; + + bool window_stalled() const; + bool window_empty() const; + + void increment_send(); + void raise(int moves); + + const uint16_t v_s() const; + const uint16_t v_s_mod(int offset) const; + const uint16_t v_a() const; + const int16_t distance() const; + + /* Methods to manage reception */ + int resend_needed(); + int mark_for_resend(); + void update(BTS *bts, char *show_rbb, uint8_t ssn, + uint16_t *lost, uint16_t *received); + int move_window(); + void show_state(char *show_rbb); + int count_unacked(); + + uint16_t m_v_s; /* send state */ + uint16_t m_v_a; /* ack state */ + + gprs_rlc_v_b m_v_b; +}; + +struct gprs_rlc_v_n { + void reset(); + + void mark_received(int bsn); + void mark_missing(int bsn); + + bool is_received(int bsn) const; + + gprs_rlc_ul_bsn_state state(int bsn) const; +private: + bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const; + void mark(int bsn, const gprs_rlc_ul_bsn_state state); + gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */ +}; + +struct gprs_rlc_ul_window { + const uint16_t mod_sns() const; + const uint16_t sns() const; + const uint16_t ws() const; + + const uint16_t v_r() const; + const uint16_t v_q() const; + + const uint16_t ssn() const; + + bool is_in_window(uint8_t bsn) const; + + void update_rbb(char *rbb); + void raise_v_r_to(int moves); + void raise_v_r(const uint16_t bsn); + uint16_t raise_v_q(); + + void raise_v_q(int); + + uint16_t receive_bsn(const uint16_t bsn); + + uint16_t m_v_r; /* receive state */ + uint16_t m_v_q; /* receive window state */ + + gprs_rlc_v_n m_v_n; +}; + +extern "C" { +/* TS 04.60 10.2.2 */ +struct rlc_ul_header { + uint8_t r:1, + si:1, + cv:4, + pt:2; + uint8_t ti:1, + tfi:5, + pi:1, + spare:1; + uint8_t e:1, + bsn:7; +} __attribute__ ((packed)); + +struct rlc_dl_header { + uint8_t usf:3, + s_p:1, + rrbp:2, + pt:2; + uint8_t fbi:1, + tfi:5, + pr:2; + uint8_t e:1, + bsn:7; +} __attribute__ ((packed)); + +struct rlc_li_field { + uint8_t e:1, + m:1, + li:6; +} __attribute__ ((packed)); +} + +inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const +{ + return m_v_b[bsn & mod_sns_half()] == type; +} + +inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type) +{ + m_v_b[bsn & mod_sns_half()] = type; +} + +inline bool gprs_rlc_v_b::is_nacked(int bsn) const +{ + return is_state(bsn, GPRS_RLC_DL_BSN_NACKED); +} + +inline bool gprs_rlc_v_b::is_acked(int bsn) const +{ + return is_state(bsn, GPRS_RLC_DL_BSN_ACKED); +} + +inline bool gprs_rlc_v_b::is_unacked(int bsn) const +{ + return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED); +} + +inline bool gprs_rlc_v_b::is_resend(int bsn) const +{ + return is_state(bsn, GPRS_RLC_DL_BSN_RESEND); +} + +inline bool gprs_rlc_v_b::is_invalid(int bsn) const +{ + return is_state(bsn, GPRS_RLC_DL_BSN_INVALID); +} + +inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const +{ + return m_v_b[bsn & mod_sns_half()]; +} + +inline void gprs_rlc_v_b::mark_resend(int bsn) +{ + return mark(bsn, GPRS_RLC_DL_BSN_RESEND); +} + +inline void gprs_rlc_v_b::mark_unacked(int bsn) +{ + return mark(bsn, GPRS_RLC_DL_BSN_UNACKED); +} + +inline void gprs_rlc_v_b::mark_acked(int bsn) +{ + return mark(bsn, GPRS_RLC_DL_BSN_ACKED); +} + +inline void gprs_rlc_v_b::mark_nacked(int bsn) +{ + return mark(bsn, GPRS_RLC_DL_BSN_NACKED); +} + +inline void gprs_rlc_v_b::mark_invalid(int bsn) +{ + return mark(bsn, GPRS_RLC_DL_BSN_INVALID); +} + +inline const uint16_t gprs_rlc_dl_window::sns() const +{ + return RLC_MAX_SNS; +} + +inline const uint16_t gprs_rlc_dl_window::ws() const +{ + return RLC_MAX_WS; +} + +inline const uint16_t gprs_rlc_dl_window::mod_sns() const +{ + return sns() - 1; +} + +inline const uint16_t gprs_rlc_dl_window::v_s() const +{ + return m_v_s; +} + +inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const +{ + return (m_v_s + offset) & mod_sns(); +} + +inline const uint16_t gprs_rlc_dl_window::v_a() const +{ + return m_v_a; +} + +inline bool gprs_rlc_dl_window::window_stalled() const +{ + return ((m_v_s - m_v_a) & mod_sns()) == ws(); +} + +inline bool gprs_rlc_dl_window::window_empty() const +{ + return m_v_s == m_v_a; +} + +inline void gprs_rlc_dl_window::increment_send() +{ + m_v_s = (m_v_s + 1) & mod_sns(); +} + +inline void gprs_rlc_dl_window::raise(int moves) +{ + m_v_a = (m_v_a + moves) & mod_sns(); +} + +inline const int16_t gprs_rlc_dl_window::distance() const +{ + return (m_v_s - m_v_a) & mod_sns(); +} + +inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const +{ + uint16_t offset_v_q; + + /* current block relative to lowest unreceived block */ + offset_v_q = (bsn - m_v_q) & mod_sns(); + /* If out of window (may happen if blocks below V(Q) are received + * again. */ + return offset_v_q < ws(); +} + +inline const uint16_t gprs_rlc_ul_window::sns() const +{ + return RLC_MAX_SNS; +} + +inline const uint16_t gprs_rlc_ul_window::ws() const +{ + return RLC_MAX_WS; +} + +inline const uint16_t gprs_rlc_ul_window::mod_sns() const +{ + return sns() - 1; +} + +inline const uint16_t gprs_rlc_ul_window::v_r() const +{ + return m_v_r; +} + +inline const uint16_t gprs_rlc_ul_window::v_q() const +{ + return m_v_q; +} + +inline const uint16_t gprs_rlc_ul_window::ssn() const +{ + return m_v_r; +} + +inline void gprs_rlc_ul_window::raise_v_r_to(int moves) +{ + m_v_r = (m_v_r + moves) & mod_sns(); +} + +inline void gprs_rlc_ul_window::raise_v_q(int incr) +{ + m_v_q = (m_v_q + incr) & mod_sns(); +} + +inline void gprs_rlc_v_n::mark_received(int bsn) +{ + return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED); +} + +inline void gprs_rlc_v_n::mark_missing(int bsn) +{ + return mark(bsn, GPRS_RLC_UL_BSN_MISSING); +} + +inline bool gprs_rlc_v_n::is_received(int bsn) const +{ + return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED); +} + +inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const +{ + return m_v_n[bsn & mod_sns_half()] == type; +} + +inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type) +{ + m_v_n[bsn & mod_sns_half()] = type; +} + +inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const +{ + return m_v_n[bsn & mod_sns_half()]; +} + +inline gprs_rlc_data *gprs_rlc::block(int bsn) +{ + return &m_blocks[bsn & mod_sns_half()]; +} diff --git a/src/sba.cpp b/src/sba.cpp new file mode 100644 index 00000000..6eec235f --- /dev/null +++ b/src/sba.cpp @@ -0,0 +1,149 @@ +/* sba.cpp + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <sba.h> +#include <gprs_rlcmac.h> +#include <gprs_debug.h> +#include <bts.h> + +extern "C" { +#include <osmocom/core/talloc.h> +} + +#include <errno.h> + +extern void *tall_pcu_ctx; + +/* starting time for assigning single slot + * This offset must be a multiple of 13. */ +#define AGCH_START_OFFSET 52 + +SBAController::SBAController(BTS &bts) + : m_bts(bts) +{ + INIT_LLIST_HEAD(&m_sbas); +} + +int SBAController::alloc( + uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta) +{ + + struct gprs_rlcmac_pdch *pdch; + struct gprs_rlcmac_sba *sba; + uint8_t trx, ts; + uint32_t fn; + + sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba); + if (!sba) + return -ENOMEM; + + for (trx = 0; trx < 8; trx++) { + for (ts = 0; ts < 8; ts++) { + pdch = &m_bts.bts_data()->trx[trx].pdch[ts]; + if (!pdch->is_enabled()) + continue; + break; + } + if (ts < 8) + break; + } + if (trx == 8) { + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n"); + talloc_free(sba); + return -EINVAL; + } + + fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648; + + sba->trx_no = trx; + sba->ts_no = ts; + sba->fn = fn; + sba->ta = ta; + + llist_add(&sba->list, &m_sbas); + m_bts.sba_allocated(); + + *_trx = trx; + *_ts = ts; + *_fn = fn; + return 0; +} + +gprs_rlcmac_sba *SBAController::find(uint8_t trx, uint8_t ts, uint32_t fn) +{ + struct gprs_rlcmac_sba *sba; + + llist_for_each_entry(sba, &m_sbas, list) { + if (sba->trx_no == trx && sba->ts_no == ts && sba->fn == fn) + return sba; + } + + return NULL; +} + +gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn) +{ + return find(pdch->trx_no(), pdch->ts_no, fn); +} + +uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr) +{ + uint32_t sba_fn; + struct gprs_rlcmac_sba *sba; + + /* check special TBF for events */ + sba_fn = fn + 4; + if ((block_nr % 3) == 2) + sba_fn ++; + sba_fn = sba_fn % 2715648; + sba = find(trx, ts, sba_fn); + if (sba) + return sba_fn; + + return 0xffffffff; +} + +int SBAController::timeout(struct gprs_rlcmac_sba *sba) +{ + LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n"); + m_bts.sba_timedout(); + free_sba(sba); + return 0; +} + +void SBAController::free_sba(gprs_rlcmac_sba *sba) +{ + m_bts.sba_freed(); + llist_del(&sba->list); + talloc_free(sba); +} + +void SBAController::free_resources(struct gprs_rlcmac_pdch *pdch) +{ + struct gprs_rlcmac_sba *sba, *sba2; + const uint8_t trx_no = pdch->trx->trx_no; + const uint8_t ts_no = pdch->ts_no; + + llist_for_each_entry_safe(sba, sba2, &m_sbas, list) { + if (sba->trx_no == trx_no && sba->ts_no == ts_no) + free_sba(sba); + } +} diff --git a/src/sba.h b/src/sba.h new file mode 100644 index 00000000..f39834a9 --- /dev/null +++ b/src/sba.h @@ -0,0 +1,69 @@ +/* + * + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#pragma once + +#include <stdint.h> + +extern "C" { +#include <osmocom/core/linuxlist.h> +} + +class BTS; +class PollController; +struct gprs_rlcmac_sba; +struct gprs_rlcmac_pdch; + +/* + * single block allocation entry + */ +struct gprs_rlcmac_sba { + struct llist_head list; + uint8_t trx_no; + uint8_t ts_no; + uint32_t fn; + uint8_t ta; +}; + +/** + * I help to manage SingleBlockAssignment (SBA). + * + * TODO: Add a flush method.. + */ +class SBAController { + friend class PollController; +public: + SBAController(BTS &bts); + + int alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta); + gprs_rlcmac_sba *find(uint8_t trx, uint8_t ts, uint32_t fn); + gprs_rlcmac_sba *find(const gprs_rlcmac_pdch *pdch, uint32_t fn); + + uint32_t sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr); + + int timeout(struct gprs_rlcmac_sba *sba); + void free_resources(struct gprs_rlcmac_pdch *pdch); + + void free_sba(gprs_rlcmac_sba *sba); + +private: + BTS &m_bts; + llist_head m_sbas; +}; diff --git a/src/sysmo_l1_hw.c b/src/sysmo_l1_hw.c index 87c2c55e..8351d68c 100644 --- a/src/sysmo_l1_hw.c +++ b/src/sysmo_l1_hw.c @@ -24,6 +24,7 @@ #include <unistd.h> #include <errno.h> #include <fcntl.h> +#include <string.h> #include <sys/types.h> #include <sys/stat.h> diff --git a/src/sysmo_l1_if.c b/src/sysmo_l1_if.c index a3a59c97..b03fad0e 100644 --- a/src/sysmo_l1_if.c +++ b/src/sysmo_l1_if.c @@ -170,8 +170,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h, gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK, data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0, - data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer, - data_ind->msgUnitParam.u8Size); + data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1, + data_ind->msgUnitParam.u8Size-1); DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s", get_value_string(femtobts_l1sapi_names, data_ind->sapi), diff --git a/src/sysmo_sock.cpp b/src/sysmo_sock.cpp index 591c105f..2e2d9d39 100644 --- a/src/sysmo_sock.cpp +++ b/src/sysmo_sock.cpp @@ -36,6 +36,7 @@ extern "C" { #include <gprs_debug.h> #include <gprs_bssgp_pcu.h> #include <pcuif_proto.h> +#include <bts.h> #include <tbf.h> extern void *tall_pcu_ctx; @@ -81,7 +82,7 @@ int pcu_sock_send(struct msgb *msg) static void pcu_sock_close(struct pcu_sock_state *state, int lost) { struct osmo_fd *bfd = &state->conn_bfd; - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct gprs_rlcmac_bts *bts = bts_main_data(); uint8_t trx, ts; LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n", @@ -106,7 +107,8 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost) } #endif for (ts = 0; ts < 8; ts++) - bts->trx[trx].pdch[ts].enable = 0; + bts->trx[trx].pdch[ts].disable(); +#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset." gprs_rlcmac_tbf::free_all(&bts->trx[trx]); } diff --git a/src/ta.cpp b/src/ta.cpp new file mode 100644 index 00000000..0bc1d660 --- /dev/null +++ b/src/ta.cpp @@ -0,0 +1,142 @@ +/* + * ta.cpp timing advance handling + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <ta.h> +#include <gprs_rlcmac.h> + +extern "C" { + #include <osmocom/core/talloc.h> +} + +#include <errno.h> + +extern void *tall_pcu_ctx; + +/* + * timing advance memory + */ + +/* enable to debug timing advance memory */ +//#define DEBUG_TA + +struct gprs_rlcmac_ta { + struct llist_head list; + uint32_t tlli; + uint8_t ta; +}; + +TimingAdvance::TimingAdvance() + : m_ta_len(0) +{ + INIT_LLIST_HEAD(&m_ta_list); +} + +/* remember timing advance of a given TLLI */ +int TimingAdvance::remember(uint32_t tlli, uint8_t ta) +{ + struct gprs_rlcmac_ta *ta_entry; + + /* check for existing entry */ + llist_for_each_entry(ta_entry, &m_ta_list, list) { + if (ta_entry->tlli == tlli) { +#ifdef DEBUG_TA + fprintf(stderr, "update %08x %d\n", tlli, ta); +#endif + ta_entry->ta = ta; + /* relink to end of list */ + llist_del(&ta_entry->list); + llist_add_tail(&ta_entry->list, &m_ta_list); + return 0; + } + } + +#ifdef DEBUG_TA + fprintf(stderr, "remember %08x %d\n", tlli, ta); +#endif + /* if list is full, remove oldest entry */ + if (m_ta_len == 30) { + ta_entry = llist_entry(m_ta_list.next, + struct gprs_rlcmac_ta, list); + llist_del(&ta_entry->list); + talloc_free(ta_entry); + m_ta_len--; + } + + /* create new TA entry */ + ta_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ta); + if (!ta_entry) + return -ENOMEM; + + ta_entry->tlli = tlli; + ta_entry->ta = ta; + llist_add_tail(&ta_entry->list, &m_ta_list); + m_ta_len++; + + return 0; +} + +int TimingAdvance::recall(uint32_t tlli) +{ + struct gprs_rlcmac_ta *ta_entry; + uint8_t ta; + + llist_for_each_entry(ta_entry, &m_ta_list, list) { + if (ta_entry->tlli == tlli) { + ta = ta_entry->ta; +#ifdef DEBUG_TA + fprintf(stderr, "recall %08x %d\n", tlli, ta); +#endif + return ta; + } + } +#ifdef DEBUG_TA + fprintf(stderr, "no entry for %08x\n", tlli); +#endif + + return -EINVAL; +} + +int TimingAdvance::flush() +{ + struct gprs_rlcmac_ta *ta_entry; + int count = 0; + + while (!llist_empty(&m_ta_list)) { + ta_entry = llist_entry(m_ta_list.next, + struct gprs_rlcmac_ta, list); +#ifdef DEBUG_TA + fprintf(stderr, "flush entry %08x %d\n", ta_entry->tlli, + ta_entry->ta); +#endif + llist_del(&ta_entry->list); + talloc_free(ta_entry); + count++; + } + m_ta_len = 0; + + return count; +} + +int TimingAdvance::update(uint32_t, uint32_t new_tlli, uint8_t ta) +{ + /* for now just add the new entry and don't bother about the old one */ + return remember(new_tlli, ta); +} diff --git a/src/ta.h b/src/ta.h new file mode 100644 index 00000000..9b64280f --- /dev/null +++ b/src/ta.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Ivan Klyuchnikov + * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu> + * Copyright (C) 2013 by Holger Hans Peter Freyther + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#pragma once + +extern "C" { +#include <osmocom/core/linuxlist.h> +} + +#include <stdint.h> + +class TimingAdvance { +public: + TimingAdvance(); + + int update(uint32_t old_tlli, uint32_t new_tlli, uint8_t ta); + int remember(uint32_t tlli, uint8_t ta); + int recall(uint32_t tlli); + int flush(); + +private: + size_t m_ta_len; + llist_head m_ta_list; +}; diff --git a/src/tbf.cpp b/src/tbf.cpp index 547612f6..d6b38022 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -19,9 +19,14 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <bts.h> #include <tbf.h> +#include <rlc.h> +#include <encoding.h> #include <gprs_rlcmac.h> #include <gprs_debug.h> +#include <gprs_bssgp_pcu.h> +#include <decoding.h> extern "C" { #include <osmocom/core/msgb.h> @@ -31,8 +36,35 @@ extern "C" { #include <errno.h> #include <string.h> +/* After sending these frames, we poll for ack/nack. */ +#define POLL_ACK_AFTER_FRAMES 20 +/* After receiving these frames, we send ack/nack. */ +#define SEND_ACK_AFTER_FRAMES 20 + + +static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = { +/* frame length data block max payload */ + { 0, 0, 0 }, + { 23, 23, 20 }, /* CS-1 */ + { 34, 33, 30 }, /* CS-2 */ + { 40, 39, 36 }, /* CS-3 */ + { 54, 53, 50 }, /* CS-4 */ +}; + +extern "C" { +int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, + uint8_t num_frames, uint32_t num_octets); +} + extern void *tall_pcu_ctx; +static void tbf_timer_cb(void *_tbf); + +inline gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const +{ + return bts->bts_data(); +} + static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf, const uint8_t ms_class) { @@ -40,67 +72,45 @@ static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf, tbf->ms_class = ms_class; } -static inline void tbf_assign_imsi(struct gprs_rlcmac_tbf *tbf, - const char *imsi) +void gprs_rlcmac_tbf::assign_imsi(const char *imsi) { - strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1); + strncpy(m_imsi, imsi, sizeof(m_imsi)); + m_imsi[sizeof(m_imsi) - 1] = '\0'; } -static struct gprs_rlcmac_tbf *tbf_lookup_dl(const uint32_t tlli, const char *imsi) +static struct gprs_rlcmac_tbf *tbf_lookup_dl(BTS *bts, + const uint32_t tlli, const char *imsi) { /* TODO: look up by IMSI first, then tlli, then old_tlli */ - return tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF); + return bts->tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF); } -static int tbf_append_data(struct gprs_rlcmac_tbf *tbf, - struct gprs_rlcmac_bts *bts, - const uint8_t ms_class, +int gprs_rlcmac_tbf::append_data(const uint8_t ms_class, const uint16_t pdu_delay_csec, const uint8_t *data, const uint16_t len) { - LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli); - if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE)) { - LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state " - "(T3193), so reuse TBF\n"); - memcpy(tbf->llc_frame, data, len); - tbf->llc_length = len; - /* reset rlc states */ - memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); - /* keep to flags */ - tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; - tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); - tbf_update_ms_class(tbf, ms_class); - tbf_update(tbf); - gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL); + LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this)); + if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) { + LOGP(DRLCMAC, LOGL_DEBUG, + "%s in WAIT RELEASE state " + "(T3193), so reuse TBF\n", tbf_name(this)); + tbf_update_ms_class(this, ms_class); + reuse_tbf(data, len); } else { /* the TBF exists, so we must write it in the queue * we prepend lifetime in front of PDU */ struct timeval *tv; - struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv), + struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2, "llc_pdu_queue"); if (!llc_msg) return -ENOMEM; tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); - - uint16_t delay_csec; - if (bts->force_llc_lifetime) - delay_csec = bts->force_llc_lifetime; - else - delay_csec = pdu_delay_csec; - /* keep timestap at 0 for infinite delay */ - if (delay_csec != 0xffff) { - /* calculate timestamp of timeout */ - gettimeofday(tv, NULL); - tv->tv_usec += (delay_csec % 100) * 10000; - tv->tv_sec += delay_csec / 100; - if (tv->tv_usec > 999999) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } - } + gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv); + tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); + gettimeofday(tv, NULL); memcpy(msgb_put(llc_msg, len), data, len); - msgb_enqueue(&tbf->llc_queue, llc_msg); - tbf_update_ms_class(tbf, ms_class); + m_llc.enqueue(llc_msg); + tbf_update_ms_class(this, ms_class); } return 0; @@ -120,10 +130,10 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, /* check for uplink data, so we copy our informations */ #warning "Do the same look up for IMSI, TLLI and OLD_TLLI" #warning "Refactor the below lines... into a new method" - tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF); + tbf = bts->bts->tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF); if (tbf && tbf->dir.ul.contention_resolution_done && !tbf->dir.ul.final_ack_sent) { - use_trx = tbf->trx; + use_trx = tbf->trx->trx_no; ta = tbf->ta; ss = 0; old_tbf = tbf; @@ -134,7 +144,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, ta = tbf->ta; else { /* recall TA */ - rc = recall_timing_advance(tlli); + rc = bts->bts->timing_advance()->recall(tlli); if (rc < 0) { LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown" ", assuming 0\n"); @@ -148,7 +158,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, // Create new TBF (any TRX) #warning "Copy and paste with alloc_ul_tbf" - tfi = tfi_find_free(bts, GPRS_RLCMAC_DL_TBF, &trx, use_trx); + tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx); if (tfi < 0) { LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); /* FIXME: send reject */ @@ -157,37 +167,35 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, /* set number of downlink slots according to multislot class */ tbf = tbf_alloc(bts, tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ms_class, ss); if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); /* FIXME: send reject */ return -EBUSY; } - tbf->tlli = tlli; - tbf->tlli_valid = 1; + tbf->m_tlli = tlli; + tbf->m_tlli_valid = 1; tbf->ta = ta; - LOGP(DRLCMAC, LOGL_DEBUG, - "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", - tbf->tfi, tbf->tlli); + LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(tbf)); /* new TBF, so put first frame */ - memcpy(tbf->llc_frame, data, len); - tbf->llc_length = len; + tbf->m_llc.put_frame(data, len); + tbf->bts->llc_frame_sched(); + + /* Store IMSI for later look-up and PCH retransmission */ + tbf->assign_imsi(imsi); /* trigger downlink assignment and set state to ASSIGN. * we don't use old_downlink, so the possible uplink is used * to trigger downlink assignment. if there is no uplink, * AGCH is used. */ - gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi); - - /* store IMSI for debugging purpose. TODO: it is more than debugging */ - tbf_assign_imsi(tbf, imsi); + tbf->bts->trigger_dl_ass(tbf, old_tbf, imsi); return 0; } /** * TODO: split into unit test-able parts... */ -int tbf_handle(struct gprs_rlcmac_bts *bts, +int gprs_rlcmac_tbf::handle(struct gprs_rlcmac_bts *bts, const uint32_t tlli, const char *imsi, const uint8_t ms_class, const uint16_t delay_csec, const uint8_t *data, const uint16_t len) @@ -195,12 +203,11 @@ int tbf_handle(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf; /* check for existing TBF */ - tbf = tbf_lookup_dl(tlli, imsi); + tbf = tbf_lookup_dl(bts->bts, tlli, imsi); if (tbf) { - int rc = tbf_append_data(tbf, bts, ms_class, - delay_csec, data, len); + int rc = tbf->append_data(ms_class, delay_csec, data, len); if (rc >= 0) - tbf_assign_imsi(tbf, imsi); + tbf->assign_imsi(imsi); return rc; } @@ -213,25 +220,25 @@ struct gprs_rlcmac_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, { uint8_t trx; struct gprs_rlcmac_tbf *tbf; - uint8_t tfi; + int8_t tfi; /* must be signed */ #warning "Copy and paste with tbf_new_dl_assignment" - /* create new TBF, use sme TRX as DL TBF */ - tfi = tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx, use_trx); + /* create new TBF, use same TRX as DL TBF */ + tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx, use_trx); if (tfi < 0) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); /* FIXME: send reject */ return NULL; } /* use multislot class of downlink TBF */ tbf = tbf_alloc(bts, dl_tbf, GPRS_RLCMAC_UL_TBF, tfi, trx, ms_class, 0); if (!tbf) { - LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); + LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n"); /* FIXME: send reject */ return NULL; } - tbf->tlli = tlli; - tbf->tlli_valid = 1; /* no contention resolution */ + tbf->m_tlli = tlli; + tbf->m_tlli_valid = 1; /* no contention resolution */ tbf->dir.ul.contention_resolution_done = 1; tbf->ta = ta; /* use current TA */ tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); @@ -243,24 +250,23 @@ struct gprs_rlcmac_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf) { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; struct gprs_rlcmac_pdch *pdch; int ts; if (tbf->direction == GPRS_RLCMAC_UL_TBF) { - bts->trx[tbf->trx].ul_tbf[tbf->tfi] = NULL; + tbf->trx->ul_tbf[tbf->tfi()] = NULL; for (ts = 0; ts < 8; ts++) { pdch = tbf->pdch[ts]; if (pdch) - pdch->ul_tbf[tbf->tfi] = NULL; + pdch->ul_tbf[tbf->tfi()] = NULL; tbf->pdch[ts] = NULL; } } else { - bts->trx[tbf->trx].dl_tbf[tbf->tfi] = NULL; + tbf->trx->dl_tbf[tbf->tfi()] = NULL; for (ts = 0; ts < 8; ts++) { pdch = tbf->pdch[ts]; if (pdch) - pdch->dl_tbf[tbf->tfi] = NULL; + pdch->dl_tbf[tbf->tfi()] = NULL; tbf->pdch[ts] = NULL; } } @@ -268,60 +274,56 @@ static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf) void tbf_free(struct gprs_rlcmac_tbf *tbf) { - struct msgb *msg; - /* Give final measurement report */ gprs_rlcmac_rssi_rep(tbf); gprs_rlcmac_lost_rep(tbf); - debug_diagram(tbf->diag, "+---------------+"); - debug_diagram(tbf->diag, "| THE END |"); - debug_diagram(tbf->diag, "+---------------+"); - LOGP(DRLCMAC, LOGL_INFO, "Free %s TBF=%d with TLLI=0x%08x.\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, - tbf->tlli); + LOGP(DRLCMAC, LOGL_INFO, "%s free\n", tbf_name(tbf)); if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE) - LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending uplink " + LOGP(DRLCMAC, LOGL_ERROR, "%s Software error: Pending uplink " "assignment. This may not happen, because the " "assignment message never gets transmitted. Please " - "be shure not to free in this state. PLEASE FIX!\n"); + "be sure not to free in this state. PLEASE FIX!\n", + tbf_name(tbf)); if (tbf->dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE) - LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending downlink " + LOGP(DRLCMAC, LOGL_ERROR, "%s Software error: Pending downlink " "assignment. This may not happen, because the " "assignment message never gets transmitted. Please " - "be shure not to free in this state. PLEASE FIX!\n"); - tbf_timer_stop(tbf); - while ((msg = msgb_dequeue(&tbf->llc_queue))) - msgb_free(msg); + "be sure not to free in this state. PLEASE FIX!\n", + tbf_name(tbf)); + tbf->stop_timer(); + #warning "TODO: Could/Should generate bssgp_tx_llc_discarded" + tbf->m_llc.clear(tbf->bts); tbf_unlink_pdch(tbf); llist_del(&tbf->list); + + if (tbf->direction == GPRS_RLCMAC_UL_TBF) + tbf->bts->tbf_ul_freed(); + else + tbf->bts->tbf_dl_freed(); + LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n"); talloc_free(tbf); } -int tbf_update(struct gprs_rlcmac_tbf *tbf) +int gprs_rlcmac_tbf::update() { - struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; struct gprs_rlcmac_tbf *ul_tbf = NULL; + struct gprs_rlcmac_bts *bts_data = bts->bts_data(); int rc; LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF update **********\n"); - if (tbf->direction != GPRS_RLCMAC_DL_TBF) + if (direction != GPRS_RLCMAC_DL_TBF) return -EINVAL; - if (!tbf->ms_class) { - LOGP(DRLCMAC, LOGL_DEBUG, "- Cannot update, no class\n"); - return -EINVAL; - } + ul_tbf = bts->tbf_by_tlli(m_tlli, GPRS_RLCMAC_UL_TBF); - ul_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF); - - tbf_unlink_pdch(tbf); - rc = bts->alloc_algorithm(bts, ul_tbf, tbf, bts->alloc_algorithm_curst, 0); - /* if no ressource */ + tbf_unlink_pdch(this); + rc = bts_data->alloc_algorithm(bts_data, ul_tbf, this, bts_data->alloc_algorithm_curst, 0); + /* if no resource */ if (rc < 0) { - LOGP(DRLCMAC, LOGL_ERROR, "No ressource after update???\n"); + LOGP(DRLCMAC, LOGL_ERROR, "No resource after update???\n"); return -rc; } @@ -353,9 +355,8 @@ static const char *tbf_state_name[] = { void tbf_new_state(struct gprs_rlcmac_tbf *tbf, enum gprs_rlcmac_tbf_state state) { - debug_diagram(tbf->diag, "->%s", tbf_state_name[state]); - LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d changes state from %s to %s\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, + LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n", + tbf_name(tbf), tbf_state_name[tbf->state], tbf_state_name[state]); tbf->set_state(state); } @@ -364,14 +365,12 @@ void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds, unsigned int microseconds) { if (!osmo_timer_pending(&tbf->timer)) - LOGP(DRLCMAC, LOGL_DEBUG, "Starting %s TBF=%d timer %u.\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", - tbf->tfi, T); + LOGP(DRLCMAC, LOGL_DEBUG, "%s starting timer %u.\n", + tbf_name(tbf), T); else - LOGP(DRLCMAC, LOGL_DEBUG, "Restarting %s TBF=%d timer %u " + LOGP(DRLCMAC, LOGL_DEBUG, "%s restarting timer %u " "while old timer %u pending \n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", - tbf->tfi, T, tbf->T); + tbf_name(tbf), T, tbf->T); tbf->T = T; tbf->num_T_exp = 0; @@ -383,80 +382,110 @@ void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, osmo_timer_schedule(&tbf->timer, seconds, microseconds); } -void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf) +void gprs_rlcmac_tbf::stop_t3191() { - if (osmo_timer_pending(&tbf->timer)) { - LOGP(DRLCMAC, LOGL_DEBUG, "Stopping %s TBF=%d timer %u.\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", - tbf->tfi, tbf->T); - osmo_timer_del(&tbf->timer); - } -} - -/* lookup TBF Entity (by TFI) */ -struct gprs_rlcmac_tbf *tbf_by_tfi(struct gprs_rlcmac_bts *bts, - uint8_t tfi, uint8_t trx, enum gprs_rlcmac_tbf_direction dir) -{ - struct gprs_rlcmac_tbf *tbf; - - if (tfi >= 32 || trx >= 8) - return NULL; - - if (dir == GPRS_RLCMAC_UL_TBF) - tbf = bts->trx[trx].ul_tbf[tfi]; - else - tbf = bts->trx[trx].dl_tbf[tfi]; - if (!tbf) - return NULL; - - if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) - return tbf; - - return NULL; + return stop_timer(); } -/* search for active downlink or uplink tbf */ -struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, - enum gprs_rlcmac_tbf_direction dir) +void gprs_rlcmac_tbf::stop_timer() { - struct gprs_rlcmac_tbf *tbf; - if (dir == GPRS_RLCMAC_UL_TBF) { - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { - if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) - && tbf->tlli == tlli && tbf->tlli_valid) - return tbf; - } - } else { - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { - if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) - && tbf->tlli == tlli) - return tbf; - } + if (osmo_timer_pending(&timer)) { + LOGP(DRLCMAC, LOGL_DEBUG, "%s stopping timer %u.\n", + tbf_name(this), T); + osmo_timer_del(&timer); } - return NULL; } -struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts) +void gprs_rlcmac_tbf::poll_timeout() { - struct gprs_rlcmac_tbf *tbf; + LOGP(DRLCMAC, LOGL_NOTICE, "%s poll timeout\n", + tbf_name(this)); - /* only one TBF can poll on specific TS/FN, because scheduler can only - * schedule one downlink control block (with polling) at a FN per TS */ - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { - if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) - && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED - && tbf->poll_fn == fn && tbf->trx == trx - && tbf->control_ts == ts) - return tbf; - } - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { - if (tbf->state_is_not(GPRS_RLCMAC_RELEASING) - && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED - && tbf->poll_fn == fn && tbf->trx == trx - && tbf->control_ts == ts) - return tbf; - } - return NULL; + poll_state = GPRS_RLCMAC_POLL_NONE; + + if (ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { + if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) { + LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " + "PACKET CONTROL ACK for PACKET UPLINK ACK\n"); + rlcmac_diag(); + state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK); + } + ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; + if (state_is(GPRS_RLCMAC_FINISHED)) { + dir.ul.n3103++; + if (dir.ul.n3103 == bts->bts_data()->n3103) { + LOGP(DRLCMAC, LOGL_NOTICE, + "- N3103 exceeded\n"); + tbf_new_state(this, GPRS_RLCMAC_RELEASING); + tbf_timer_start(this, 3169, bts->bts_data()->t3169, 0); + return; + } + /* reschedule UL ack */ + ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; + } + } else if (ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) { + if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { + LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " + "PACKET CONTROL ACK for PACKET UPLINK " + "ASSIGNMENT.\n"); + rlcmac_diag(); + state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS); + } + ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; + n3105++; + if (n3105 == bts_data()->n3105) { + LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); + tbf_new_state(this, GPRS_RLCMAC_RELEASING); + tbf_timer_start(this, 3195, bts_data()->t3195, 0); + return; + } + /* reschedule UL assignment */ + ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; + } else if (dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) { + if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) { + LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " + "PACKET CONTROL ACK for PACKET DOWNLINK " + "ASSIGNMENT.\n"); + rlcmac_diag(); + state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS); + } + dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; + n3105++; + if (n3105 == bts->bts_data()->n3105) { + LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); + tbf_new_state(this, GPRS_RLCMAC_RELEASING); + tbf_timer_start(this, 3195, bts_data()->t3195, 0); + return; + } + /* reschedule DL assignment */ + dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; + } else if (direction == GPRS_RLCMAC_DL_TBF) { + if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) { + LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " + "PACKET DOWNLINK ACK.\n"); + rlcmac_diag(); + state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); + } + n3105++; + if (n3105 == bts->bts_data()->n3105) { + LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); + tbf_new_state(this, GPRS_RLCMAC_RELEASING); + tbf_timer_start(this, 3195, bts_data()->t3195, 0); + return; + } + /* resend IMM.ASS on CCCH on timeout */ + if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) + && !(state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) { + LOGP(DRLCMAC, LOGL_DEBUG, "Re-send dowlink assignment " + "for %s on PCH (IMSI=%s)\n", + tbf_name(this), + m_imsi); + /* send immediate assignment */ + bts->snd_dl_ass(this, 0, m_imsi); + dir.dl.wait_confirm = 1; + } + } else + LOGP(DRLCMAC, LOGL_ERROR, "- Poll Timeout, but no event!\n"); } struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts, @@ -467,24 +496,6 @@ struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf; int rc; -#ifdef DEBUG_DIAGRAM - /* hunt for first free number in diagram */ - int diagram_num; - for (diagram_num = 0; ; diagram_num++) { - llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) { - if (tbf->diag == diagram_num) - goto next_diagram; - } - llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) { - if (tbf->diag == diagram_num) - goto next_diagram; - } - break; -next_diagram: - continue; - } -#endif - LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF starts here **********\n"); LOGP(DRLCMAC, LOGL_INFO, "Allocating %s TBF: TFI=%d TRX=%d " "MS_CLASS=%d\n", (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", @@ -497,20 +508,16 @@ next_diagram: if (!tbf) return NULL; -#ifdef DEBUG_DIAGRAM - tbf->diag = diagram_num; -#endif + tbf->m_created_ts = time(NULL); + tbf->bts = bts->bts; tbf->direction = dir; - tbf->tfi = tfi; - tbf->trx = trx; - tbf->arfcn = bts->trx[trx].arfcn; + tbf->m_tfi = tfi; + tbf->trx = &bts->trx[trx]; tbf->ms_class = ms_class; - tbf->ws = 64; - tbf->sns = 128; /* select algorithm */ rc = bts->alloc_algorithm(bts, old_tbf, tbf, bts->alloc_algorithm_curst, single_slot); - /* if no ressource */ + /* if no resource */ if (rc < 0) { talloc_free(tbf); return NULL; @@ -518,7 +525,7 @@ next_diagram: /* assign control ts */ tbf->control_ts = 0xff; rc = tbf_assign_control_ts(tbf); - /* if no ressource */ + /* if no resource */ if (rc < 0) { talloc_free(tbf); return NULL; @@ -529,74 +536,71 @@ next_diagram: gettimeofday(&tbf->meas.rssi_tv, NULL); gettimeofday(&tbf->meas.dl_loss_tv, NULL); - INIT_LLIST_HEAD(&tbf->llc_queue); - if (dir == GPRS_RLCMAC_UL_TBF) - llist_add(&tbf->list, &gprs_rlcmac_ul_tbfs); - else - llist_add(&tbf->list, &gprs_rlcmac_dl_tbfs); - - debug_diagram(tbf->diag, "+-----------------+"); - debug_diagram(tbf->diag, "|NEW %s TBF TFI=%2d|", - (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi); - debug_diagram(tbf->diag, "+-----------------+"); + tbf->m_llc.init(); + if (dir == GPRS_RLCMAC_UL_TBF) { + llist_add(&tbf->list, &bts->ul_tbfs); + tbf->bts->tbf_ul_created(); + } else { + llist_add(&tbf->list, &bts->dl_tbfs); + tbf->bts->tbf_dl_created(); + } return tbf; } -void tbf_timer_cb(void *_tbf) +static void tbf_timer_cb(void *_tbf) { struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf; + tbf->handle_timeout(); +} - LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d timer %u expired.\n", - (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, - tbf->T); +void gprs_rlcmac_tbf::handle_timeout() +{ + LOGP(DRLCMAC, LOGL_DEBUG, "%s timer %u expired.\n", + tbf_name(this), T); - tbf->num_T_exp++; + num_T_exp++; - switch (tbf->T) { -#ifdef DEBUG_DL_ASS_IDLE - case 1234: - gprs_rlcmac_trigger_downlink_assignment(tbf, NULL, debug_imsi); - break; -#endif + switch (T) { case 0: /* assignment */ - if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) { - if (tbf->state_is(GPRS_RLCMAC_ASSIGN)) { - LOGP(DRLCMAC, LOGL_NOTICE, "Releasing due to " - "PACCH assignment timeout.\n"); - tbf_free(tbf); + if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) { + if (state_is(GPRS_RLCMAC_ASSIGN)) { + LOGP(DRLCMAC, LOGL_NOTICE, "%s releasing due to " + "PACCH assignment timeout.\n", tbf_name(this)); + tbf_free(this); + return; } else - LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not " - "in assign state\n"); + LOGP(DRLCMAC, LOGL_ERROR, "Error: %s is not " + "in assign state\n", tbf_name(this)); } - if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { + if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { /* change state to FLOW, so scheduler will start transmission */ - tbf->dir.dl.wait_confirm = 0; - if (tbf->state_is(GPRS_RLCMAC_ASSIGN)) { - tbf_new_state(tbf, GPRS_RLCMAC_FLOW); - tbf_assign_control_ts(tbf); + dir.dl.wait_confirm = 0; + if (state_is(GPRS_RLCMAC_ASSIGN)) { + tbf_new_state(this, GPRS_RLCMAC_FLOW); + tbf_assign_control_ts(this); } else - LOGP(DRLCMAC, LOGL_NOTICE, "Continue flow after " - "IMM.ASS confirm\n"); + LOGP(DRLCMAC, LOGL_NOTICE, "%s Continue flow after " + "IMM.ASS confirm\n", tbf_name(this)); } break; case 3169: case 3191: case 3195: - LOGP(DRLCMAC, LOGL_NOTICE, "TBF T%d timeout during " - "transsmission\n", tbf->T); - tbf->rlcmac_diag(); + LOGP(DRLCMAC, LOGL_NOTICE, "%s T%d timeout during " + "transsmission\n", tbf_name(this), T); + rlcmac_diag(); /* fall through */ case 3193: - if (tbf->T == 3193) - debug_diagram(tbf->diag, "T3193 timeout"); - LOGP(DRLCMAC, LOGL_DEBUG, "TBF will be freed due to timeout\n"); + LOGP(DRLCMAC, LOGL_DEBUG, + "%s will be freed due to timeout\n", tbf_name(this)); /* free TBF */ - tbf_free(tbf); + tbf_free(this); + return; break; default: - LOGP(DRLCMAC, LOGL_ERROR, "Timer expired in unknown mode: %u\n", - tbf->T); + LOGP(DRLCMAC, LOGL_ERROR, + "%s timer expired in unknown mode: %u\n", tbf_name(this), T); } } @@ -618,6 +622,818 @@ int gprs_rlcmac_tbf::rlcmac_diag() return 0; } +struct msgb *gprs_rlcmac_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) +{ + struct msgb *msg; + struct timeval *tv, tv_now; + uint32_t octets = 0, frames = 0; + + gettimeofday(&tv_now, NULL); + + while ((msg = m_llc.dequeue())) { + tv = (struct timeval *)msg->data; + msgb_pull(msg, sizeof(*tv)); + msgb_pull(msg, sizeof(*tv)); + + if (gprs_llc::is_frame_expired(&tv_now, tv)) { + LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU " + "because lifetime limit reached. Queue size %zu\n", + tbf_name(this), m_llc.m_queue_size); + bts->llc_timedout_frame(); + frames++; + octets += msg->len; + msgb_free(msg); + continue; + } + break; + } + + if (frames) { + if (frames > 0xff) + frames = 0xff; + if (octets > 0xffffff) + octets = 0xffffff; + bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets); + } + + return msg; +} + +/* + * Store received block data in LLC message(s) and forward to SGSN + * if complete. + */ +int gprs_rlcmac_tbf::assemble_forward_llc(const gprs_rlc_data *_data) +{ + const uint8_t *data = _data->block; + uint8_t len = _data->len; + const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data; + uint8_t e, m; + struct rlc_li_field *li; + uint8_t frame_offset[16], offset = 0, chunk; + int i, frames = 0; + + LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len); + + data += 3; + len -= 3; + e = rh->e; /* if extended */ + m = 1; /* more frames, that means: the first frame */ + + /* Parse frame offsets from length indicator(s), if any. */ + while (1) { + if (frames == (int)sizeof(frame_offset)) { + LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in " + "block\n", tbf_name(this)); + return -EINVAL; + } + frame_offset[frames++] = offset; + LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset " + "%d\n", frames, offset); + if (!len) + break; + /* M == 0 and E == 0 is not allowed in this version. */ + if (!m && !e) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA " + "ignored, because M='0' and E='0'.\n", + tbf_name(this)); + return 0; + } + /* no more frames in this segment */ + if (e) { + break; + } + /* There is a new frame and an LI that delimits it. */ + if (m) { + li = (struct rlc_li_field *)data; + LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n", + li->li); + /* Special case: LI == 0 + * If the last segment would fit precisely into the + * rest of the RLC MAC block, there would be no way + * to delimit that this segment ends and is not + * continued in the next block. + * The special LI (0) is used to force the segment to + * extend into the next block, so it is delimited there. + * This LI must be skipped. Also it is the last LI. + */ + if (li->li == 0) { + data++; + len--; + m = 1; /* M is ignored, we know there is more */ + break; /* handle E as '1', so we break! */ + } + e = li->e; + m = li->m; + offset += li->li; + data++; + len--; + continue; + } + } + if (!m) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare " + "data\n"); + } + + LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n", + len); + /* TLLI */ + if (rh->ti) { + if (len < 4) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of " + "frame border\n", tbf_name(this)); + return -EINVAL; + } + data += 4; + len -= 4; + LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: " + "%d\n", len); + } + + /* PFI */ + if (rh->pi) { + LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, " + "please disable in SYSTEM INFORMATION\n"); + if (len < 1) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of " + "frame border\n", tbf_name(this)); + return -EINVAL; + } + data++; + len--; + LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: " + "%d\n", len); + } + + /* Now we have: + * - a list of frames offsets: frame_offset[] + * - number of frames: i + * - m == 0: Last frame carries spare data (end of TBF). + */ + + /* Check if last offset would exceed frame. */ + if (offset > len) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, " + "because LI delimits data that exceeds block size.\n", + tbf_name(this)); + return -EINVAL; + } + + /* create LLC frames */ + for (i = 0; i < frames; i++) { + /* last frame ? */ + if (i == frames - 1) { + /* no more data in last frame */ + if (!m) + break; + /* data until end of frame */ + chunk = len - frame_offset[i]; + } else { + /* data until next frame */ + chunk = frame_offset[i + 1] - frame_offset[i]; + } + if (!m_llc.fits_in_current_frame(chunk)) { + LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds " + "maximum size %u.\n", tbf_name(this), + m_llc.remaining_space()); + chunk = m_llc.remaining_space(); + } + m_llc.append_frame(data + frame_offset[i], chunk); + m_llc.consume(chunk); + /* not last frame. */ + if (i != frames - 1) { + /* send frame to SGSN */ + LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n", + tbf_name(this) , m_llc.frame_length()); + snd_ul_ud(); + m_llc.reset(); + /* also check if CV==0, because the frame may fill up the + * block precisely, then it is also complete. normally the + * frame would be extended into the next block with a 0-length + * delimiter added to this block. */ + } else if (rh->cv == 0) { + /* send frame to SGSN */ + LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame " + "that fits precisely in last block: " + "len=%d\n", tbf_name(this), m_llc.frame_length()); + snd_ul_ud(); + m_llc.reset(); + } + } + + return 0; +} + +/* + * Create DL data block + * The messages are fragmented and forwarded as data blocks. + */ +struct msgb *gprs_rlcmac_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts) +{ + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. " + "V(S)==%d)\n", tbf_name(this), + dir.dl.window.v_a(), dir.dl.window.v_s()); + +do_resend: + /* check if there is a block with negative acknowledgement */ + int resend_bsn = dir.dl.window.resend_needed(); + if (resend_bsn >= 0) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn); + /* re-send block with negative aknowlegement */ + dir.dl.window.m_v_b.mark_unacked(resend_bsn); + bts->rlc_resent(); + return create_dl_acked_block(fn, ts, resend_bsn, false); + } + + /* if the window has stalled, or transfer is complete, + * send an unacknowledged block */ + if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) { + if (state_is(GPRS_RLCMAC_FINISHED)) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " + "because all blocks have been transmitted.\n", + dir.dl.window.v_a()); + bts->rlc_restarted(); + } else { + LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " + "because all window is stalled.\n", + dir.dl.window.v_a()); + bts->rlc_stalled(); + } + /* If V(S) == V(A) and finished state, we would have received + * acknowledgement of all transmitted block. In this case we + * would have transmitted the final block, and received ack + * from MS. But in this case we did not receive the final ack + * indication from MS. This should never happen if MS works + * correctly. */ + if (dir.dl.window.window_empty()) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " + "so we re-transmit final block!\n"); + /* we just send final block again */ + int16_t index = dir.dl.window.v_s_mod(-1); + bts->rlc_resent(); + return create_dl_acked_block(fn, ts, index, false); + } + + /* cycle through all unacked blocks */ + int resend = dir.dl.window.mark_for_resend(); + + /* At this point there should be at least one unacked block + * to be resent. If not, this is an software error. */ + if (resend == 0) { + LOGP(DRLCMACDL, LOGL_ERROR, "Software error: " + "There are no unacknowledged blocks, but V(A) " + " != V(S). PLEASE FIX!\n"); + /* we just send final block again */ + int16_t index = dir.dl.window.v_s_mod(-1); + return create_dl_acked_block(fn, ts, index, false); + } + goto do_resend; + } + + return create_new_bsn(fn, ts); +} + +struct msgb *gprs_rlcmac_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts) +{ + struct rlc_dl_header *rh; + struct rlc_li_field *li; + struct msgb *msg; + uint8_t *delimiter, *data, *e_pointer; + uint16_t space, chunk; + gprs_rlc_data *rlc_data; + bool first_fin_ack = false; + const uint16_t bsn = dir.dl.window.v_s(); + + LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n", + dir.dl.window.v_s()); + +#warning "Selection of the CS doesn't belong here" + if (cs == 0) { + cs = bts_data()->initial_cs_dl; + if (cs < 1 || cs > 4) + cs = 1; + } + /* total length of block, including spare bits */ + const uint8_t block_length = gprs_rlcmac_cs[cs].block_length; + /* length of usable data of block, w/o spare bits, inc. MAC */ + const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data; + + /* now we still have untransmitted LLC data, so we fill mac block */ + rlc_data = m_rlc.block(bsn); + data = rlc_data->prepare(block_data_len); + + rh = (struct rlc_dl_header *)data; + rh->pt = 0; /* Data Block */ + rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */ + rh->usf = 7; /* will be set at scheduler */ + rh->pr = 0; /* FIXME: power reduction */ + rh->tfi = m_tfi; /* TFI */ + rh->fbi = 0; /* Final Block Indicator, set late, if true */ + rh->bsn = bsn; /* Block Sequence Number */ + rh->e = 0; /* Extension bit, maybe set later */ + e_pointer = data + 2; /* points to E of current chunk */ + data += sizeof(*rh); + delimiter = data; /* where next length header would be stored */ + space = block_data_len - sizeof(*rh); + while (1) { + chunk = m_llc.chunk_size(); + /* if chunk will exceed block limit */ + if (chunk > space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "larger than space (%d) left in block: copy " + "only remaining space, and we are done\n", + chunk, space); + /* block is filled, so there is no extension */ + *e_pointer |= 0x01; + /* fill only space */ + m_llc.consume(data, space); + /* return data block as message */ + break; + } + /* if FINAL chunk would fit precisely in space left */ + if (chunk == space && llist_empty(&m_llc.queue)) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): because " + "this is a final block, we don't add length " + "header, and we are done\n", chunk, space); + LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " + "%s that fits precisely in last block: " + "len=%d\n", tbf_name(this), m_llc.frame_length()); + gprs_rlcmac_dl_bw(this, m_llc.frame_length()); + /* block is filled, so there is no extension */ + *e_pointer |= 0x01; + /* fill space */ + m_llc.consume(data, space); + m_llc.reset(); + /* final block */ + rh->fbi = 1; /* we indicate final block */ + tbf_new_state(this, GPRS_RLCMAC_FINISHED); + /* return data block as message */ + break; + } + /* if chunk would fit exactly in space left */ + if (chunk == space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): add length " + "header with LI=0, to make frame extend to " + "next block, and we are done\n", chunk, space); + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, + data - delimiter); + data++; + space--; + /* add LI with 0 length */ + li = (struct rlc_li_field *)delimiter; + li->e = 1; /* not more extension */ + li->m = 0; /* shall be set to 0, in case of li = 0 */ + li->li = 0; /* chunk fills the complete space */ + // no need to set e_pointer nor increase delimiter + /* fill only space, which is 1 octet less than chunk */ + m_llc.consume(data, space); + /* return data block as message */ + break; + } + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " + "than remaining space (%d): add length header to " + "to delimit LLC frame\n", chunk, space); + /* the LLC frame chunk ends in this block */ + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, data - delimiter); + data++; + space--; + /* add LI to delimit frame */ + li = (struct rlc_li_field *)delimiter; + li->e = 0; /* Extension bit, maybe set later */ + li->m = 0; /* will be set later, if there is more LLC data */ + li->li = chunk; /* length of chunk */ + e_pointer = delimiter; /* points to E of current delimiter */ + delimiter++; + /* copy (rest of) LLC frame to space and reset later */ + m_llc.consume(data, chunk); + data += chunk; + space -= chunk; + LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s" + "len=%d\n", tbf_name(this), m_llc.frame_length()); + gprs_rlcmac_dl_bw(this, m_llc.frame_length()); + m_llc.reset(); + /* dequeue next LLC frame, if any */ + msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); + if (msg) { + LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " + "%s (len=%d)\n", tbf_name(this), msg->len); + m_llc.put_frame(msg->data, msg->len); + bts->llc_frame_sched(); + msgb_free(msg); + } + /* if we have more data and we have space left */ + if (space > 0 && m_llc.frame_length()) { + li->m = 1; /* we indicate more frames to follow */ + continue; + } + /* if we don't have more LLC frames */ + if (!m_llc.frame_length()) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " + "done.\n"); + li->e = 1; /* we cannot extend */ + rh->fbi = 1; /* we indicate final block */ + first_fin_ack = true; + /* + 1 indicates: first final ack */ + tbf_new_state(this, GPRS_RLCMAC_FINISHED); + break; + } + /* we have no space left */ + LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " + "done.\n"); + li->e = 1; /* we cannot extend */ + break; + } + LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n", + osmo_hexdump(rlc_data->block, block_length)); +#warning "move this up?" + rlc_data->len = block_length; + /* raise send state and set ack state array */ + dir.dl.window.m_v_b.mark_unacked(bsn); + dir.dl.window.increment_send(); + + return create_dl_acked_block(fn, ts, bsn, first_fin_ack); +} + +struct msgb *gprs_rlcmac_tbf::create_dl_acked_block( + const uint32_t fn, const uint8_t ts, + const int index, const bool first_fin_ack) +{ + uint8_t *data; + struct rlc_dl_header *rh; + struct msgb *dl_msg; + uint8_t len; + + /* get data and header from current block */ + data = m_rlc.block(index)->block; + len = m_rlc.block(index)->len; + rh = (struct rlc_dl_header *)data; + + /* Clear Polling, if still set in history buffer */ + rh->s_p = 0; + + /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx. + */ + if (dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) { + if (first_fin_ack) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " + "polling, because first final block sent.\n"); + } else { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " + "polling, because %d blocks sent.\n", + POLL_ACK_AFTER_FRAMES); + } + /* scheduling not possible, because: */ + if (poll_state != GPRS_RLCMAC_POLL_NONE) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already " + "sheduled for %s, so we must wait for " + "requesting downlink ack\n", tbf_name(this)); + else if (control_ts != ts) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " + "sheduled in this TS %d, waiting for " + "TS %d\n", ts, control_ts); +#warning "What happens to the first_fin_ack in case something is already scheduled?" + else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648)) + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " + "sheduled, because single block alllocation " + "already exists\n"); + else { + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this " + "TS %d\n", ts); + dir.dl.tx_counter = 0; + /* start timer whenever we send the final block */ + if (rh->fbi == 1) + tbf_timer_start(this, 3191, bts_data()->t3191, 0); + + /* schedule polling */ + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + + /* set polling in header */ + rh->rrbp = 0; /* N+13 */ + rh->s_p = 1; /* Polling */ + } + } + + /* return data block as message */ + dl_msg = msgb_alloc(len, "rlcmac_dl_data"); + if (!dl_msg) + return NULL; + + /* Increment TX-counter */ + dir.dl.tx_counter++; + + memcpy(msgb_put(dl_msg, len), data, len); + bts->rlc_sent(); + + return dl_msg; +} + +struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn) +{ + struct msgb *msg; + struct gprs_rlcmac_tbf *new_tbf; + int poll_ass_dl = 1; + + if (direction == GPRS_RLCMAC_DL_TBF && control_ts != first_common_ts) { + LOGP(DRLCMAC, LOGL_NOTICE, "Cannot poll for downlink " + "assigment, because MS cannot reply. (control TS=%d, " + "first common TS=%d)\n", control_ts, + first_common_ts); + poll_ass_dl = 0; + } + if (poll_ass_dl) { + if (poll_state != GPRS_RLCMAC_POLL_NONE) { + LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already sheduled " + "for %s, so we must wait for downlink " + "assignment...\n", tbf_name(this)); + return NULL; + } + if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "scheduled for single block allocation...\n"); + return NULL; + } + } + + /* on uplink TBF we get the downlink TBF to be assigned. */ + if (direction == GPRS_RLCMAC_UL_TBF) { + /* be sure to check first, if contention resolution is done, + * otherwise we cannot send the assignment yet */ + if (!dir.ul.contention_resolution_done) { + LOGP(DRLCMAC, LOGL_DEBUG, "Cannot assign DL TBF now, " + "because contention resolution is not " + "finished.\n"); + return NULL; + } + #warning "THIS should probably go over the IMSI too" + new_tbf = bts->tbf_by_tlli(m_tlli, GPRS_RLCMAC_DL_TBF); + } else + new_tbf = this; + if (!new_tbf) { + LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink " + "assignment at uplink %s, but there is no downlink " + "TBF\n", tbf_name(this)); + dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; + return NULL; + } + + msg = msgb_alloc(23, "rlcmac_dl_ass"); + if (!msg) + return NULL; + bitvec *ass_vec = bitvec_alloc(23); + if (!ass_vec) { + msgb_free(msg); + return NULL; + } + bitvec_unhex(ass_vec, + "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + LOGP(DRLCMAC, LOGL_INFO, "%s start Packet Downlink Assignment (PACCH)\n", tbf_name(new_tbf)); + RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); + Encoding::write_packet_downlink_assignment(mac_control_block, m_tfi, + (direction == GPRS_RLCMAC_DL_TBF), new_tbf, + poll_ass_dl, bts_data()->alpha, bts_data()->gamma, -1, 0); + LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n"); + encode_gsm_rlcmac_downlink(ass_vec, mac_control_block); + LOGPC(DCSN1, LOGL_NOTICE, "\n"); + LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n"); + bitvec_pack(ass_vec, msgb_put(msg, 23)); + bitvec_free(ass_vec); + talloc_free(mac_control_block); + + if (poll_ass_dl) { + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK; + } else { + dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; + tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW); + tbf_assign_control_ts(new_tbf); + /* stop pending assignment timer */ + new_tbf->stop_timer(); + + } + + return msg; +} + +struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn) +{ + struct msgb *msg; + struct gprs_rlcmac_tbf *new_tbf; + + if (poll_state != GPRS_RLCMAC_POLL_NONE) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "sheduled for %s, so we must wait for uplink " + "assignment...\n", tbf_name(this)); + return NULL; + } + if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already scheduled for " + "single block allocation...\n"); + return NULL; + } + + /* on down TBF we get the uplink TBF to be assigned. */ +#warning "Probably want to find by IMSI too" + if (direction == GPRS_RLCMAC_DL_TBF) + new_tbf = bts->tbf_by_tlli(m_tlli, GPRS_RLCMAC_UL_TBF); + else + new_tbf = this; + + if (!new_tbf) { + LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink " + "assignment at downlink %s, but there is no uplink " + "TBF\n", tbf_name(this)); + ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; + return NULL; + } + + msg = msgb_alloc(23, "rlcmac_ul_ass"); + if (!msg) + return NULL; + LOGP(DRLCMAC, LOGL_INFO, "%ss start Packet Uplink Assignment (PACCH)\n", tbf_name(new_tbf)); + bitvec *ass_vec = bitvec_alloc(23); + if (!ass_vec) { + msgb_free(msg); + return NULL; + } + bitvec_unhex(ass_vec, + "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + Encoding::write_packet_uplink_assignment(bts_data(), ass_vec, m_tfi, + (direction == GPRS_RLCMAC_DL_TBF), m_tlli, + m_tlli_valid, new_tbf, 1, bts_data()->alpha, + bts_data()->gamma, -1); + bitvec_pack(ass_vec, msgb_put(msg, 23)); + RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); + LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Uplink Assignment +++++++++++++++++++++++++\n"); + decode_gsm_rlcmac_downlink(ass_vec, mac_control_block); + LOGPC(DCSN1, LOGL_NOTICE, "\n"); + LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n"); + bitvec_free(ass_vec); + talloc_free(mac_control_block); + + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK; + + return msg; +} + +struct msgb *gprs_rlcmac_tbf::create_ul_ack(uint32_t fn) +{ + int final = (state_is(GPRS_RLCMAC_FINISHED)); + struct msgb *msg; + + if (final) { + if (poll_state != GPRS_RLCMAC_POLL_NONE) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "sheduled for %s, so we must wait for " + "final uplink ack...\n", tbf_name(this)); + return NULL; + } + if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " + "scheduled for single block allocation...\n"); + return NULL; + } + } + + msg = msgb_alloc(23, "rlcmac_ul_ack"); + if (!msg) + return NULL; + bitvec *ack_vec = bitvec_alloc(23); + if (!ack_vec) { + msgb_free(msg); + return NULL; + } + bitvec_unhex(ack_vec, + "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); + RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); + Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final); + encode_gsm_rlcmac_downlink(ack_vec, mac_control_block); + bitvec_pack(ack_vec, msgb_put(msg, 23)); + bitvec_free(ack_vec); + talloc_free(mac_control_block); + + /* now we must set this flag, so we are allowed to assign downlink + * TBF on PACCH. it is only allowed when TLLI is acknowledged. */ + dir.ul.contention_resolution_done = 1; + + if (final) { + poll_state = GPRS_RLCMAC_POLL_SCHED; + poll_fn = (fn + 13) % 2715648; + /* waiting for final acknowledge */ + ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; + dir.ul.final_ack_sent = 1; + } else + ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; + + return msg; +} + +int gprs_rlcmac_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) +{ + int16_t dist; /* must be signed */ + uint16_t lost = 0, received = 0; + char show_rbb[65]; + char show_v_b[RLC_MAX_SNS + 1]; + const uint16_t mod_sns = dir.dl.window.mod_sns(); + + Decoding::extract_rbb(rbb, show_rbb); + /* show received array in debug (bit 64..1) */ + LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" + "(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns, + show_rbb, (ssn - 1) & mod_sns); + + /* apply received array to receive state (SSN-64..SSN-1) */ + /* calculate distance of ssn from V(S) */ + dist = (dir.dl.window.v_s() - ssn) & mod_sns; + /* check if distance is less than distance V(A)..V(S) */ + if (dist >= dir.dl.window.distance()) { + /* this might happpen, if the downlink assignment + * was not received by ms and the ack refers + * to previous TBF + * FIXME: we should implement polling for + * control ack!*/ + LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " + "V(A)..V(S) range %s Free TBF!\n", tbf_name(this)); + return 1; /* indicate to free TBF */ + } + + dir.dl.window.update(bts, show_rbb, ssn, + &lost, &received); + + /* report lost and received packets */ + gprs_rlcmac_received_lost(this, received, lost); + + /* raise V(A), if possible */ + dir.dl.window.raise(dir.dl.window.move_window()); + + /* show receive state array in debug (V(A)..V(S)-1) */ + dir.dl.window.show_state(show_v_b); + LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" + "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " + "X=Resend-Unacked I=Invalid\n", + dir.dl.window.v_a(), show_v_b, + dir.dl.window.v_s_mod(-1)); + + if (state_is(GPRS_RLCMAC_FINISHED) && dir.dl.window.window_empty()) { + LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " + "all blocks, but without final ack " + "inidcation (don't worry)\n"); + } + return 0; +} + + +int gprs_rlcmac_tbf::maybe_start_new_window() +{ + struct msgb *msg; + uint16_t received; + + LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); + /* range V(A)..V(S)-1 */ + received = dir.dl.window.count_unacked(); + + /* report all outstanding packets as received */ + gprs_rlcmac_received_lost(this, received, 0); + + /* check for LLC PDU in the LLC Queue */ + msg = llc_dequeue(gprs_bssgp_pcu_current_bctx()); + if (!msg) { + /* no message, start T3193, change state to RELEASE */ + LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n"); + /* start T3193 */ + tbf_timer_start(this, 3193, + bts_data()->t3193_msec / 1000, + (bts_data()->t3193_msec % 1000) * 1000); + tbf_new_state(this, GPRS_RLCMAC_WAIT_RELEASE); + + return 0; + } + + /* we have more data so we will re-use this tbf */ + reuse_tbf(msg->data, msg->len); + msgb_free(msg); + return 0; +} + +int gprs_rlcmac_tbf::snd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb) +{ + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this)); + + if (!final_ack) + return update_window(ssn, rbb); + return maybe_start_new_window(); +} + void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_trx *trx) { for (uint8_t tfi = 0; tfi < 32; tfi++) { @@ -645,3 +1461,321 @@ void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_pdch *pdch) tbf_free(tbf); } } + +void gprs_rlcmac_tbf::tlli_mark_valid() +{ + m_tlli_valid = true; +} + +void gprs_rlcmac_tbf::update_tlli(uint32_t tlli) +{ + if (tlli == m_tlli) + return; + + bool changedUl = false; + + /* + * During a Routing Area Update (due the assignment of a new + * P-TMSI) the tlli can change. We notice this when receiving + * a PACKET CONTROL ACK. + * When we get a TLLI change on the DL we will look if there + * is a UL TBF and change the tlli there as well. + * + * TODO: There could be multiple DL and UL TBFs and we should + * have a proper way to link all the related TBFs so we can do + * a group update. + */ + if (m_tlli_valid && direction == GPRS_RLCMAC_DL_TBF) { + gprs_rlcmac_tbf *ul_tbf; + ul_tbf = bts->tbf_by_tlli(m_tlli, GPRS_RLCMAC_UL_TBF); + + if (ul_tbf) { + ul_tbf->m_tlli = tlli; + changedUl = true; + } + } + + /* update the timing advance for the new tlli */ + bts->timing_advance()->update(m_tlli, tlli, ta); + + LOGP(DRLCMAC, LOGL_NOTICE, + "%s changing tlli from TLLI=0x%08x TLLI=0x%08x ul_changed=%d\n", + tbf_name(this), m_tlli, tlli, changedUl); + m_tlli = tlli; +} + +int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len) +{ + struct gprs_rlcmac_tbf *dl_tbf, *ul_tbf; + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + uint32_t new_tlli; + int rc; + + /* no TLLI yet */ + if (!rh->ti) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TFI=%d without " + "TLLI, but no TLLI received yet\n", rh->tfi); + return 0; + } + rc = Decoding::tlli_from_ul_data(data, len, &new_tlli); + if (rc) { + bts->decode_error(); + LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " + "of UL DATA TFI=%d.\n", rh->tfi); + return 0; + } + update_tlli(new_tlli); + LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of " + "UL DATA TFI=%d.\n", tlli(), rh->tfi); + if ((dl_tbf = bts->tbf_by_tlli(tlli(), GPRS_RLCMAC_DL_TBF))) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while %s still exists. " + "Killing pending DL TBF\n", tlli(), + tbf_name(dl_tbf)); + tbf_free(dl_tbf); + dl_tbf = NULL; + } + /* tbf_by_tlli will not find your TLLI, because it is not + * yet marked valid */ + if ((ul_tbf = bts->tbf_by_tlli(tlli(), GPRS_RLCMAC_UL_TBF))) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while %s still exists. " + "Killing pending UL TBF\n", tlli(), + tbf_name(ul_tbf)); + tbf_free(ul_tbf); + ul_tbf = NULL; + } + /* mark TLLI valid now */ + tlli_mark_valid(); + /* store current timing advance */ + bts->timing_advance()->remember(tlli(), ta); + return 1; +} + +int gprs_rlcmac_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi) +{ + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + int rc; + + const uint16_t mod_sns = dir.ul.window.mod_sns(); + const uint16_t ws = dir.ul.window.ws(); + + this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA); + + LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. " + "V(R)=%d)\n", rh->tfi, this->dir.ul.window.v_q(), + this->dir.ul.window.v_r()); + + /* process RSSI */ + gprs_rlcmac_rssi(this, rssi); + + /* get TLLI */ + if (!this->is_tlli_valid()) { + if (!extract_tlli(data, len)) + return 0; + /* already have TLLI, but we stille get another one */ + } else if (rh->ti) { + uint32_t tlli; + rc = Decoding::tlli_from_ul_data(data, len, &tlli); + if (rc) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " + "of UL DATA TFI=%d.\n", rh->tfi); + return 0; + } + if (tlli != this->tlli()) { + LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " + "DATA TFI=%d. (Ignoring due to contention " + "resolution)\n", rh->tfi); + return 0; + } + } + + /* restart T3169 */ + tbf_timer_start(this, 3169, bts_data()->t3169, 0); + + /* Increment RX-counter */ + this->dir.ul.rx_counter++; + + if (!dir.ul.window.is_in_window(rh->bsn)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " + "%d..%d (it's normal)\n", rh->bsn, + dir.ul.window.v_q(), + (dir.ul.window.v_q() + ws - 1) & mod_sns); + maybe_schedule_uplink_acknack(rh); + return 0; + } + + /* Write block to buffer and set receive state array. */ + m_rlc.block(rh->bsn)->put_data(data, len); + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", + rh->bsn, dir.ul.window.v_q(), + (dir.ul.window.v_q() + ws - 1) & mod_sns); + + /* Raise V(Q) if possible, and retrieve LLC frames from blocks. + * This is looped until there is a gap (non received block) or + * the window is empty.*/ + const uint16_t v_q_beg = dir.ul.window.v_q(); + + const uint16_t count = dir.ul.window.receive_bsn(rh->bsn); + + /* Retrieve LLC frames from blocks that are ready */ + for (uint16_t i = 0; i < count; ++i) { + uint16_t index = (v_q_beg + i) & mod_sns; + assemble_forward_llc(m_rlc.block(index)); + } + + /* Check CV of last frame in buffer */ + if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */ + && this->dir.ul.window.v_q() == this->dir.ul.window.v_r()) { /* if complete */ + struct rlc_ul_header *last_rh = (struct rlc_ul_header *) + m_rlc.block((dir.ul.window.v_r() - 1) & mod_sns)->block; + LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " + "last block: BSN=%d CV=%d\n", last_rh->bsn, + last_rh->cv); + if (last_rh->cv == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " + "TBF\n"); + tbf_new_state(this, GPRS_RLCMAC_FINISHED); + /* Reset N3103 counter. */ + this->dir.ul.n3103 = 0; + } + } + + /* If TLLI is included or if we received half of the window, we send + * an ack/nack */ + maybe_schedule_uplink_acknack(rh); + + return 0; +} + +void gprs_rlcmac_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh) +{ + if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED) + || (dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + if (rh->si) { + LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " + "because MS is stalled.\n"); + } + if (rh->ti) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because TLLI is included.\n"); + } + if (state_is(GPRS_RLCMAC_FINISHED)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because last block has CV==0.\n"); + } + if ((dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because %d frames received.\n", + SEND_ACK_AFTER_FRAMES); + } + if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) { + /* trigger sending at next RTS */ + ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; + } else { + /* already triggered */ + LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is " + "already triggered, don't schedule!\n"); + } + } +} + +/* Send Uplink unit-data to SGSN. */ +int gprs_rlcmac_tbf::snd_ul_ud() +{ + uint8_t qos_profile[3]; + struct msgb *llc_pdu; + unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length(); + struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); + + LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length()); + if (!bctx) { + LOGP(DBSSGP, LOGL_ERROR, "No bctx\n"); + m_llc.reset_frame_space(); + return -EIO; + } + + llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu"); + uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length())); + tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame); + qos_profile[0] = QOS_PROFILE >> 16; + qos_profile[1] = QOS_PROFILE >> 8; + qos_profile[2] = QOS_PROFILE; + bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu); + + m_llc.reset_frame_space(); + return 0; +} + +const char *tbf_name(gprs_rlcmac_tbf *tbf) +{ + static char buf[40]; + snprintf(buf, sizeof(buf), "TBF(TFI=%d TLLI=0x%08x DIR=%s)", + tbf->m_tfi, tbf->m_tlli, + tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL"); + buf[sizeof(buf) - 1] = '\0'; + return buf; +} + + +void gprs_rlcmac_tbf::reuse_tbf(const uint8_t *data, const uint16_t len) +{ + bts->tbf_reused(); + m_llc.put_frame(data, len); + bts->llc_frame_sched(); + + /* reset rlc states */ + memset(&dir.dl, 0, sizeof(dir.dl)); + dir.dl.window.m_v_b.reset(); + + /* keep to flags */ + state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; + state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); + + update(); + + LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, " + "because another LLC PDU has arrived in between\n", + tbf_name(this)); + bts->trigger_dl_ass(this, this, NULL); +} + +bool gprs_rlcmac_tbf::dl_window_stalled() const +{ + return dir.dl.window.window_stalled(); +} + +void gprs_rlcmac_tbf::rotate_in_list() +{ + llist_del(&list); + if (direction == GPRS_RLCMAC_UL_TBF) + llist_add(&list, &bts->bts_data()->ul_tbfs); + else + llist_add(&list, &bts->bts_data()->dl_tbfs); +} + +uint8_t gprs_rlcmac_tbf::tsc() const +{ + return trx->pdch[first_ts].tsc; +} + +void tbf_print_vty_info(struct vty *vty, llist_head *ltbf) +{ + gprs_rlcmac_tbf *tbf = llist_entry(ltbf, gprs_rlcmac_tbf, list); + + vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) DIR=%s IMSI=%s%s", tbf->tfi(), + tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid", + tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL", + tbf->imsi(), VTY_NEWLINE); + vty_out(vty, " created=%lu state=%08x 1st_TS=%d 1st_cTS=%d ctrl_TS=%d " + "MS_CLASS=%d%s", + tbf->created_ts(), tbf->state_flags, tbf->first_ts, + tbf->first_common_ts, tbf->control_ts, tbf->ms_class, + VTY_NEWLINE); + vty_out(vty, " TS_alloc="); + for (int i = 0; i < 8; i++) { + if (tbf->pdch[i]) + vty_out(vty, "%d ", i); + } + vty_out(vty, " CS=%d%s%s", tbf->cs, VTY_NEWLINE, VTY_NEWLINE); +} @@ -18,19 +18,22 @@ #pragma once +#ifdef __cplusplus + #include "gprs_rlcmac.h" +#include "llc.h" +#include "rlc.h" #include <stdint.h> +struct bssgp_bvc_ctx; +struct rlc_ul_header; +struct msgb; + /* * TBF instance */ -#define LLC_MAX_LEN 1543 -#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */ -#define RLC_MAX_WS 64 /* max window size */ -#define RLC_MAX_LEN 54 /* CS-4 including spare bits */ - #define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */ #define Tassign_pacch 2,0 /* timeout for pacch assigment */ @@ -81,11 +84,6 @@ enum gprs_rlcmac_tbf_direction { #define GPRS_RLCMAC_FLAG_TO_DL_ASS 7 #define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */ -extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */ -extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */ -extern struct llist_head gprs_rlcmac_sbas; /* list of single block allocs */ - - struct gprs_rlcmac_tbf { static void free_all(struct gprs_rlcmac_trx *trx); @@ -95,17 +93,60 @@ struct gprs_rlcmac_tbf { bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const; void set_state(enum gprs_rlcmac_tbf_state new_state); + /* TODO: add the gettimeofday as parameter */ + struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx); + + /* TODO: extract LLC class? */ + int assemble_forward_llc(const gprs_rlc_data *data); + + struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts); + struct msgb *create_dl_ass(uint32_t fn); + struct msgb *create_ul_ass(uint32_t fn); + struct msgb *create_ul_ack(uint32_t fn); + int snd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb); + int snd_ul_ud(); + + /* blocks were acked */ + int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi); + + /* dispatch Unitdata.DL messages */ + static int handle(struct gprs_rlcmac_bts *bts, + const uint32_t tlli, const char *imsi, const uint8_t ms_class, + const uint16_t delay_csec, const uint8_t *data, const uint16_t len); + + uint8_t tsc() const; + int rlcmac_diag(); + int update(); + void handle_timeout(); + void stop_timer(); + void stop_t3191(); + + void poll_timeout(); + + /** tlli handling */ + void update_tlli(uint32_t tlli); + uint32_t tlli() const; + bool is_tlli_valid() const; + void tlli_mark_valid(); + + uint8_t tfi() const; + + const char *imsi() const; + void assign_imsi(const char *imsi); + + uint16_t sns() const; + + time_t created_ts() const; + + /* attempt to make things a bit more fair */ + void rotate_in_list(); + struct llist_head list; uint32_t state_flags; enum gprs_rlcmac_tbf_direction direction; - uint8_t tfi; - uint32_t tlli; - uint8_t tlli_valid; - uint8_t trx; - uint16_t arfcn; - uint8_t tsc; + struct gprs_rlcmac_trx *trx; uint8_t first_ts; /* first TS used by TBF */ uint8_t first_common_ts; /* first TS that the phone can send and reveive simultaniously */ @@ -113,10 +154,8 @@ struct gprs_rlcmac_tbf { uint8_t ms_class; struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */ uint16_t ta; - uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */ - uint16_t llc_index; /* current write/read position of frame */ - uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */ - struct llist_head llc_queue; /* queued LLC DL data */ + + gprs_llc m_llc; enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state; enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state; @@ -125,9 +164,6 @@ struct gprs_rlcmac_tbf { enum gprs_rlcmac_tbf_poll_state poll_state; uint32_t poll_fn; /* frame number to poll */ - uint16_t ws; /* window size */ - uint16_t sns; /* sequence number space */ - /* Please note that all variables here will be reset when changing * from WAIT RELEASE back to FLOW state (re-use of TBF). * All states that need reset must be in this struct, so this is why @@ -135,19 +171,12 @@ struct gprs_rlcmac_tbf { */ union { struct { - uint16_t bsn; /* block sequence number */ - uint16_t v_s; /* send state */ - uint16_t v_a; /* ack state */ - char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */ + gprs_rlc_dl_window window; int32_t tx_counter; /* count all transmitted blocks */ - char imsi[16]; /* store IMSI for PCH retransmission */ uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */ } dl; struct { - uint16_t bsn; /* block sequence number */ - uint16_t v_r; /* receive state */ - uint16_t v_q; /* receive window state */ - char v_n[RLC_MAX_SNS/2]; /* receive state array */ + gprs_rlc_ul_window window; int32_t rx_counter; /* count all received blocks */ uint8_t n3103; /* N3103 counter */ uint8_t usf[8]; /* list USFs per PDCH (timeslot) */ @@ -155,8 +184,8 @@ struct gprs_rlcmac_tbf { uint8_t final_ack_sent; /* set if we sent final ack */ } ul; } dir; - uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */ - uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */ + + gprs_rlc m_rlc; uint8_t n3105; /* N3105 counter */ @@ -169,8 +198,6 @@ struct gprs_rlcmac_tbf { unsigned int num_fT_exp; /* number of consecutive fT expirations */ struct { - char imsi[16]; - struct timeval dl_bw_tv; /* timestamp for dl bw calculation */ uint32_t dl_bw_octets; /* number of octets since bw_tv */ @@ -186,47 +213,56 @@ struct gprs_rlcmac_tbf { uint8_t cs; /* current coding scheme */ -#ifdef DEBUG_DIAGRAM - int diag; /* number where TBF is presented in diagram */ - int diag_new; /* used to format output of new TBF */ -#endif - /* these should become protected but only after gprs_rlcmac_data.c * stops to iterate over all tbf in its current form */ enum gprs_rlcmac_tbf_state state; -}; + /* store the BTS this TBF belongs to */ + BTS *bts; + + /* + * private fields. We can't make it private as it is breaking the + * llist macros. + */ + uint32_t m_tlli; + uint8_t m_tlli_valid; + uint8_t m_tfi; + time_t m_created_ts; + + /* store IMSI for look-up and PCH retransmission */ + char m_imsi[16]; + +protected: + int update_window(const uint8_t ssn, const uint8_t *rbb); + int maybe_start_new_window(); + void reuse_tbf(const uint8_t *data, const uint16_t len); + gprs_rlcmac_bts *bts_data() const; + bool dl_window_stalled() const; + + int extract_tlli(const uint8_t *data, const size_t len); + void maybe_schedule_uplink_acknack(const rlc_ul_header *rh); + + int append_data(const uint8_t ms_class, + const uint16_t pdu_delay_csec, + const uint8_t *data, const uint16_t len); + + struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts, + const int index, const bool fin_first_ack); + struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts); +}; -/* dispatch Unitdata.DL messages */ -int tbf_handle(struct gprs_rlcmac_bts *bts, - const uint32_t tlli, const char *imsi, const uint8_t ms_class, - const uint16_t delay_csec, const uint8_t *data, const uint16_t len); struct gprs_rlcmac_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, int8_t use_trx, uint8_t ms_class, uint32_t tlli, uint8_t ta, struct gprs_rlcmac_tbf *dl_tbf); -int tfi_find_free(struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir, - uint8_t *_trx, int8_t use_trx); - struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *old_tbf, enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx, uint8_t ms_class, uint8_t single_slot); -struct gprs_rlcmac_tbf *tbf_by_tfi(struct gprs_rlcmac_bts *bts, - uint8_t tfi, uint8_t trx, - enum gprs_rlcmac_tbf_direction dir); - -struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, - enum gprs_rlcmac_tbf_direction dir); - -struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts); - void tbf_free(struct gprs_rlcmac_tbf *tbf); -int tbf_update(struct gprs_rlcmac_tbf *tbf); - int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf); void tbf_new_state(struct gprs_rlcmac_tbf *tbf, @@ -235,11 +271,6 @@ void tbf_new_state(struct gprs_rlcmac_tbf *tbf, void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds, unsigned int microseconds); -void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf); - -void tbf_timer_cb(void *_tbf); - - inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const { return state == rhs; @@ -254,3 +285,50 @@ inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state) { state = new_state; } + +inline uint32_t gprs_rlcmac_tbf::tlli() const +{ + return m_tlli; +} + +inline bool gprs_rlcmac_tbf::is_tlli_valid() const +{ + return m_tlli_valid; +} + +inline uint8_t gprs_rlcmac_tbf::tfi() const +{ + return m_tfi; +} + +inline const char *gprs_rlcmac_tbf::imsi() const +{ + return m_imsi; +} + +inline uint16_t gprs_rlcmac_tbf::sns() const +{ + /* assume dl/ul do the same thing */ + return dir.dl.window.sns(); +} + +const char *tbf_name(gprs_rlcmac_tbf *tbf); + +inline time_t gprs_rlcmac_tbf::created_ts() const +{ + return m_created_ts; +} + +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#include <osmocom/vty/command.h> +#include <osmocom/vty/vty.h> + + + void tbf_print_vty_info(struct vty *vty, llist_head *tbf); +#ifdef __cplusplus +} +#endif |