| /* |
| * 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_v_b::resend_needed(const gprs_rlc_dl_window &w) |
| { |
| for (uint16_t bsn = w.v_a(); bsn != w.v_s(); bsn = (bsn + 1) & w.mod_sns()) { |
| if (is_nacked(bsn) || is_resend(bsn)) |
| return bsn; |
| } |
| |
| return -1; |
| } |
| |
| int gprs_rlc_v_b::mark_for_resend(const gprs_rlc_dl_window &w) |
| { |
| int resend = 0; |
| |
| for (uint16_t bsn = w.v_a(); bsn != w.v_s(); bsn = (bsn + 1) & w.mod_sns()) { |
| if (is_unacked(bsn)) { |
| /* mark to be re-send */ |
| mark_resend(bsn); |
| resend += 1; |
| } |
| } |
| |
| return resend; |
| } |
| |
| int gprs_rlc_v_b::count_unacked(const gprs_rlc_dl_window &w) |
| { |
| uint16_t unacked = 0; |
| uint16_t bsn; |
| |
| for (bsn = w.v_a(); bsn != w.v_s(); bsn = (bsn + 1) & w.mod_sns()) { |
| if (!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_v_b::update(BTS *bts, char *show_rbb, uint8_t ssn, |
| const gprs_rlc_dl_window &w, |
| uint16_t *lost, uint16_t *received) |
| { |
| /* SSN - 1 is in range V(A)..V(S)-1 */ |
| for (int bitpos = 0; bitpos < w.ws(); bitpos++) { |
| uint16_t bsn = bitnum_to_bsn(bitpos, ssn, w.mod_sns()); |
| |
| if (bsn == ((w.v_a() - 1) & w.mod_sns())) |
| break; |
| |
| if (show_rbb[w.ws() - 1 - bitpos] == 'R') { |
| LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn); |
| if (!is_acked(bsn)) |
| *received += 1; |
| mark_acked(bsn); |
| } else { |
| LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn); |
| mark_nacked(bsn); |
| bts->rlc_nacked(); |
| *lost += 1; |
| } |
| } |
| } |
| |
| int gprs_rlc_v_b::move_window(const gprs_rlc_dl_window &w) |
| { |
| int i; |
| uint16_t bsn; |
| int moved = 0; |
| |
| for (i = 0, bsn = w.v_a(); bsn != w.v_s(); i++, bsn = (bsn + 1) & w.mod_sns()) { |
| if (is_acked(bsn)) { |
| mark_invalid(bsn); |
| moved += 1; |
| } else |
| break; |
| } |
| |
| return moved; |
| } |
| |
| void gprs_rlc_v_b::state(char *show_v_b, const gprs_rlc_dl_window &w) |
| { |
| int i; |
| uint16_t bsn; |
| |
| for (i = 0, bsn = w.v_a(); bsn != w.v_s(); i++, bsn = (bsn + 1) & w.mod_sns()) { |
| uint16_t index = bsn & mod_sns_half(); |
| show_v_b[i] = m_v_b[index]; |
| if (show_v_b[i] == 0) |
| show_v_b[i] = ' '; |
| } |
| show_v_b[i] = '\0'; |
| } |
| |
| void gprs_rlc_v_n::reset() |
| { |
| memset(m_v_n, 0x0, sizeof(m_v_n)); |
| } |
| |
| /* Update the receive block bitmap */ |
| void gprs_rlc_ul_window::update_rbb(const gprs_rlc_v_n *v_n, char *rbb) |
| { |
| int i; |
| for (i=0; i < ws(); i++) { |
| if (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, gprs_rlc_v_n *v_n) |
| { |
| 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 */ |
| v_n->mark_missing(v_r()); |
| raise_v_r(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(gprs_rlc_v_n *v_n) |
| { |
| uint16_t count = 0; |
| |
| while (v_q() != v_r()) { |
| if (!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; |
| } |