/* Copied from gprs_bssgp_pcu.cpp * * Copyright (C) 2012 Ivan Klyuchnikov * Copyright (C) 2012 Andreas Eversberg * 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 #include #include #include #include #include #include extern "C" { #include #include } #include #include /* If acknowledgement to downlink assignment should be polled */ #define POLLING_ASSIGNMENT_DL 1 #define POLLING_ASSIGNMENT_UL 1 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) { if (!tbf->ms_class && ms_class) tbf->ms_class = ms_class; } static inline void tbf_assign_imsi(struct gprs_rlcmac_tbf *tbf, const char *imsi) { strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1); } 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 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, 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->bts->trigger_dl_ass(tbf, tbf, NULL); } 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), "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++; } } memcpy(msgb_put(llc_msg, len), data, len); msgb_enqueue(&tbf->llc_queue, llc_msg); tbf_update_ms_class(tbf, ms_class); } return 0; } static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, const char *imsi, const uint32_t tlli, const uint8_t ms_class, const uint8_t *data, const uint16_t len) { uint8_t trx, ta, ss; int8_t use_trx; struct gprs_rlcmac_tbf *old_tbf, *tbf; int8_t tfi; /* must be signed */ int rc; /* 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 = 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_no; ta = tbf->ta; ss = 0; old_tbf = tbf; } else { use_trx = -1; /* we already have an uplink TBF, so we use that TA */ if (tbf) ta = tbf->ta; else { /* recall TA */ rc = bts->bts->timing_advance()->recall(tlli); if (rc < 0) { LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown" ", assuming 0\n"); ta = 0; } else ta = rc; } ss = 1; /* PCH assignment only allows one timeslot */ old_tbf = NULL; } // Create new TBF (any TRX) #warning "Copy and paste with alloc_ul_tbf" 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 */ return -EBUSY; } /* 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"); /* FIXME: send reject */ return -EBUSY; } tbf->tlli = tlli; tbf->tlli_valid = 1; tbf->ta = ta; LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli); /* new TBF, so put first frame */ memcpy(tbf->llc_frame, data, len); tbf->llc_length = len; /* 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. */ tbf->bts->trigger_dl_ass(tbf, old_tbf, imsi); /* store IMSI for debugging purpose. TODO: it is more than debugging */ tbf_assign_imsi(tbf, imsi); return 0; } /** * TODO: split into unit test-able parts... */ 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; /* check for existing TBF */ tbf = tbf_lookup_dl(bts->bts, tlli, imsi); if (tbf) { int rc = tbf_append_data(tbf, bts, ms_class, delay_csec, data, len); if (rc >= 0) tbf_assign_imsi(tbf, imsi); return rc; } return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, 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) { uint8_t trx; struct gprs_rlcmac_tbf *tbf; uint8_t tfi; #warning "Copy and paste with tbf_new_dl_assignment" /* create new TBF, use sme 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"); /* 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"); /* FIXME: send reject */ return NULL; } tbf->tlli = tlli; tbf->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); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); tbf_timer_start(tbf, 3169, bts->t3169, 0); return tbf; } static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf) { struct gprs_rlcmac_pdch *pdch; int ts; if (tbf->direction == GPRS_RLCMAC_UL_TBF) { 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; tbf->pdch[ts] = NULL; } } else { 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; tbf->pdch[ts] = NULL; } } } 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->bts, tbf->diag, "+---------------+"); debug_diagram(tbf->bts, tbf->diag, "| THE END |"); debug_diagram(tbf->bts, 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); if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE) LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending uplink " "assignment. This may not happen, because the " "assignment message never gets transmitted. Please " "be sure not to free in this state. PLEASE FIX!\n"); if (tbf->dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE) LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending downlink " "assignment. This may not happen, because the " "assignment message never gets transmitted. Please " "be sure not to free in this state. PLEASE FIX!\n"); tbf->stop_timer(); #warning "TODO: Could/Should generate bssgp_tx_llc_discarded" while ((msg = msgb_dequeue(&tbf->llc_queue))) msgb_free(msg); tbf_unlink_pdch(tbf); llist_del(&tbf->list); LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n"); talloc_free(tbf); } int gprs_rlcmac_tbf::update() { 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 (direction != GPRS_RLCMAC_DL_TBF) return -EINVAL; if (!ms_class) { LOGP(DRLCMAC, LOGL_DEBUG, "- Cannot update, no class\n"); return -EINVAL; } ul_tbf = bts->tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF); tbf_unlink_pdch(this); rc = bts_data->alloc_algorithm(bts_data, ul_tbf, this, bts_data->alloc_algorithm_curst, 0); /* if no ressource */ if (rc < 0) { LOGP(DRLCMAC, LOGL_ERROR, "No ressource after update???\n"); return -rc; } return 0; } int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf) { if (tbf->control_ts == 0xff) LOGP(DRLCMAC, LOGL_INFO, "- Setting Control TS %d\n", tbf->first_common_ts); else if (tbf->control_ts != tbf->first_common_ts) LOGP(DRLCMAC, LOGL_INFO, "- Changing Control TS %d\n", tbf->first_common_ts); tbf->control_ts = tbf->first_common_ts; return 0; } static const char *tbf_state_name[] = { "NULL", "ASSIGN", "FLOW", "FINISHED", "WAIT RELEASE", "RELEASING", }; void tbf_new_state(struct gprs_rlcmac_tbf *tbf, enum gprs_rlcmac_tbf_state state) { debug_diagram(tbf->bts, 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, tbf_state_name[tbf->state], tbf_state_name[state]); tbf->set_state(state); } 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); else LOGP(DRLCMAC, LOGL_DEBUG, "Restarting %s TBF=%d timer %u " "while old timer %u pending \n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, T, tbf->T); tbf->T = T; tbf->num_T_exp = 0; /* Tunning timers can be safely re-scheduled. */ tbf->timer.data = tbf; tbf->timer.cb = &tbf_timer_cb; osmo_timer_schedule(&tbf->timer, seconds, microseconds); } void gprs_rlcmac_tbf::stop_t3191() { return stop_timer(); } void gprs_rlcmac_tbf::stop_timer() { if (osmo_timer_pending(&timer)) { LOGP(DRLCMAC, LOGL_DEBUG, "Stopping %s TBF=%d timer %u.\n", (direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi, T); osmo_timer_del(&timer); } } void gprs_rlcmac_tbf::poll_timeout() { LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for %s TBF=%d\n", (direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi); 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; debug_diagram(bts, this->diag, "timeout UL-ACK"); if (state_is(GPRS_RLCMAC_FINISHED)) { dir.ul.n3103++; if (dir.ul.n3103 == bts->bts_data()->n3103) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3103 exceeded\n"); debug_diagram(bts, diag, "N3103 exceeded"); 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; debug_diagram(bts, diag, "timeout UL-ASS"); n3105++; if (n3105 == bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts, diag, "N3105 exceeded"); 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; debug_diagram(bts, diag, "timeout DL-ASS"); n3105++; if (n3105 == bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts, diag, "N3105 exceeded"); 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); } debug_diagram(bts, diag, "timeout DL-ACK"); n3105++; if (n3105 == bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts, diag, "N3105 exceeded"); 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 TBF=%d on PCH (IMSI=%s)\n", tfi, dir.dl.imsi); /* send immediate assignment */ bts->snd_dl_ass(this, 0, dir.dl.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, 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; 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, &bts->ul_tbfs, list) { if (tbf->diag == diagram_num) goto next_diagram; } llist_for_each_entry(tbf, &bts->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", tfi, trx, ms_class); if (trx >= 8 || tfi >= 32) return NULL; tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_tbf); if (!tbf) return NULL; tbf->bts = bts->bts; #ifdef DEBUG_DIAGRAM tbf->diag = diagram_num; #endif tbf->direction = dir; tbf->tfi = tfi; tbf->trx_no = trx; tbf->trx = &bts->trx[trx]; tbf->arfcn = bts->trx[trx].arfcn; 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 (rc < 0) { talloc_free(tbf); return NULL; } /* assign control ts */ tbf->control_ts = 0xff; rc = tbf_assign_control_ts(tbf); /* if no ressource */ if (rc < 0) { talloc_free(tbf); return NULL; } /* set timestamp */ gettimeofday(&tbf->meas.dl_bw_tv, NULL); 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, &bts->ul_tbfs); else llist_add(&tbf->list, &bts->dl_tbfs); debug_diagram(bts->bts, tbf->diag, "+-----------------+"); debug_diagram(bts->bts, tbf->diag, "|NEW %s TBF TFI=%2d|", (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi); debug_diagram(bts->bts, tbf->diag, "+-----------------+"); return tbf; } static void tbf_timer_cb(void *_tbf) { struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf; tbf->handle_timeout(); } void gprs_rlcmac_tbf::handle_timeout() { LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d timer %u expired.\n", (direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi, T); num_T_exp++; switch (T) { case 0: /* assignment */ if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) { if (state_is(GPRS_RLCMAC_ASSIGN)) { LOGP(DRLCMAC, LOGL_NOTICE, "Releasing due to " "PACCH assignment timeout.\n"); tbf_free(this); } else LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not " "in assign state\n"); } if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { /* change state to FLOW, so scheduler will start transmission */ 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"); } break; case 3169: case 3191: case 3195: LOGP(DRLCMAC, LOGL_NOTICE, "TBF T%d timeout during " "transsmission\n", T); rlcmac_diag(); /* fall through */ case 3193: if (T == 3193) debug_diagram(bts, diag, "T3193 timeout"); LOGP(DRLCMAC, LOGL_DEBUG, "TBF will be freed due to timeout\n"); /* free TBF */ tbf_free(this); break; default: LOGP(DRLCMAC, LOGL_ERROR, "Timer expired in unknown mode: %u\n", T); } } int gprs_rlcmac_tbf::rlcmac_diag() { if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) LOGP(DRLCMAC, LOGL_NOTICE, "- Assignment was on CCCH\n"); if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) LOGP(DRLCMAC, LOGL_NOTICE, "- Assignment was on PACCH\n"); if ((state_flags & (1 << GPRS_RLCMAC_FLAG_UL_DATA))) LOGP(DRLCMAC, LOGL_NOTICE, "- Uplink data was received\n"); else if (direction == GPRS_RLCMAC_UL_TBF) LOGP(DRLCMAC, LOGL_NOTICE, "- No uplink data received yet\n"); if ((state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) LOGP(DRLCMAC, LOGL_NOTICE, "- Downlink ACK was received\n"); else if (direction == GPRS_RLCMAC_DL_TBF) LOGP(DRLCMAC, LOGL_NOTICE, "- No downlink ACK received yet\n"); 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 = msgb_dequeue(&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", 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(bctx, tlli, frames, octets); } return msg; } void gprs_rlcmac_tbf::update_llc_frame(struct msgb *msg) { /* TODO: bounds check */ memcpy(llc_frame, msg->data, msg->len); llc_length = msg->len; } /* * Store received block data in LLC message(s) and forward to SGSN * if complete. */ int gprs_rlcmac_tbf::assemble_forward_llc(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", this->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", this->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, this->llc_index); if (this->llc_index + chunk > LLC_MAX_LEN) { LOGP(DRLCMACUL, LOGL_NOTICE, "LLC frame exceeds " "maximum size.\n"); chunk = LLC_MAX_LEN - this->llc_index; } memcpy(this->llc_frame + this->llc_index, data + frame_offset[i], chunk); this->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", this->tfi, this->llc_index); gprs_rlcmac_tx_ul_ud(this); this->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", this->tfi, this->llc_index); gprs_rlcmac_tx_ul_ud(this); this->llc_index = 0; /* reset frame space */ } } return 0; } struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn) { struct msgb *msg; struct gprs_rlcmac_tbf *new_tbf; int poll_ass_dl = POLLING_ASSIGNMENT_DL; if (poll_ass_dl && 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 TBF=%d, so we must wait for downlink " "assignment...\n", tfi); return NULL; } if (bts->sba()->find(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(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 TBF=%d, but there is no downlink " "TBF\n", tfi); 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); Encoding::write_packet_downlink_assignment(mac_control_block, 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(); } debug_diagram(bts, diag, "send DL-ASS"); return msg; } struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn) { struct msgb *msg; struct gprs_rlcmac_tbf *new_tbf; #if POLLING_ASSIGNMENT_UL == 1 if (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", tfi); return NULL; } if (bts->sba()->find(trx_no, 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. */ #warning "Probably want to find by IMSI too" if (direction == GPRS_RLCMAC_DL_TBF) new_tbf = bts->tbf_by_tlli(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 TBF=%d, but there is no uplink " "TBF\n", tfi); 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"); Encoding::write_packet_uplink_assignment(bts_data(), ass_vec, tfi, (direction == GPRS_RLCMAC_DL_TBF), tlli, tlli_valid, new_tbf, POLLING_ASSIGNMENT_UL, 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); #if POLLING_ASSIGNMENT_UL == 1 poll_state = GPRS_RLCMAC_POLL_SCHED; poll_fn = (fn + 13) % 2715648; ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK; #else 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(bts, diag, "send UL-ASS"); 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 TBF=%d, so we must wait for " "final uplink ack...\n", tfi); return NULL; } if (bts->sba()->find(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; debug_diagram(bts->bts, diag, "send UL-ACK"); return msg; } int gprs_rlcmac_tbf::snd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb) { char show_rbb[65], show_v_b[RLC_MAX_SNS + 1]; uint16_t mod_sns = sns - 1; uint16_t mod_sns_half = (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", 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 = (dir.dl.v_s - ssn) & mod_sns; /* check if distance is less than distance V(A)..V(S) */ if (dist >= ((dir.dl.v_s - 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", 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 != ((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 (dir.dl.v_b[bsn & mod_sns_half] != 'A') received++; dir.dl.v_b[bsn & mod_sns_half] = 'A'; } else { LOGP(DRLCMACDL, LOGL_DEBUG, "- got " "NACK for BSN=%d\n", bsn); dir.dl.v_b[bsn & mod_sns_half] = 'N'; lost++; } } /* report lost and received packets */ gprs_rlcmac_received_lost(this, received, lost); /* raise V(A), if possible */ for (i = 0, bsn = dir.dl.v_a; bsn != dir.dl.v_s; i++, bsn = (bsn + 1) & mod_sns) { if (dir.dl.v_b[bsn & mod_sns_half] == 'A') { dir.dl.v_b[bsn & mod_sns_half] = 'I'; /* mark invalid */ dir.dl.v_a = (dir.dl.v_a + 1) & mod_sns; } else break; } /* show receive state array in debug (V(A)..V(S)-1) */ for (i = 0, bsn = dir.dl.v_a; bsn != dir.dl.v_s; i++, bsn = (bsn + 1) & mod_sns) { show_v_b[i] = 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", dir.dl.v_a, show_v_b, (dir.dl.v_s - 1) & mod_sns); if (state_is(GPRS_RLCMAC_FINISHED) && dir.dl.v_s == 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(ts, diag, "got Final ACK"); /* range V(A)..V(S)-1 */ for (bsn = dir.dl.v_a; bsn != dir.dl.v_s; bsn = (bsn + 1) & mod_sns) { if (dir.dl.v_b[bsn & mod_sns_half] != 'A') received++; } /* report all outstanding packets as received */ gprs_rlcmac_received_lost(this, received, lost); /* 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 */ debug_diagram(bts, diag, "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; } #warning "Copy and paste on the sender path" update_llc_frame(msg); 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(&dir.dl, 0, sizeof(dir.dl)); /* reset RLC states */ state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep TO flags */ state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); update(); bts->trigger_dl_ass(this, this, NULL); return 0; } void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_trx *trx) { for (uint8_t tfi = 0; tfi < 32; tfi++) { struct gprs_rlcmac_tbf *tbf; tbf = trx->ul_tbf[tfi]; if (tbf) tbf_free(tbf); tbf = trx->dl_tbf[tfi]; if (tbf) tbf_free(tbf); } } void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_pdch *pdch) { for (uint8_t tfi = 0; tfi < 32; tfi++) { struct gprs_rlcmac_tbf *tbf; tbf = pdch->ul_tbf[tfi]; if (tbf) tbf_free(tbf); tbf = pdch->dl_tbf[tfi]; if (tbf) tbf_free(tbf); } }