forked from cellular-infrastructure/osmo-pcu
809 lines
23 KiB
C++
809 lines
23 KiB
C++
/*
|
|
* 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>
|
|
}
|
|
|
|
const uint8_t* one_run_len_code_list[MAX_CDWDTBL_LEN] = {
|
|
(uint8_t*)"00110101", (uint8_t*)"000111", (uint8_t*)"0111", (uint8_t*)"1000",
|
|
(uint8_t*) "1011", (uint8_t*)"1100", (uint8_t*)"1110", (uint8_t*)"1111",
|
|
(uint8_t*)"10011", (uint8_t*)"10100", (uint8_t*)"00111", (uint8_t*)"01000",
|
|
(uint8_t*)"001000", (uint8_t*)"000011", (uint8_t*)"110100", (uint8_t*)"110101",
|
|
(uint8_t*)"101010", (uint8_t*)"101011", (uint8_t*)"0100111", (uint8_t*)"0001100",
|
|
(uint8_t*)"0001000", (uint8_t*)"0010111", (uint8_t*)"0000011", (uint8_t*)"0000100",
|
|
(uint8_t*)"0101000", (uint8_t*)"0101011", (uint8_t*)"0010011", (uint8_t*)"0100100",
|
|
(uint8_t*)"0011000", (uint8_t*)"00000010", (uint8_t*)"00000011", (uint8_t*)"00011010",
|
|
(uint8_t*)"00011011", (uint8_t*)"00010010", (uint8_t*)"00010011", (uint8_t*)"00010100",
|
|
(uint8_t*)"00010101", (uint8_t*)"00010110", (uint8_t*)"00010111", (uint8_t*)"00101000",
|
|
(uint8_t*)"00101001", (uint8_t*)"00101010", (uint8_t*)"00101011", (uint8_t*)"00101100",
|
|
(uint8_t*)"00101101", (uint8_t*)"00000100", (uint8_t*)"00000101", (uint8_t*)"00001010",
|
|
(uint8_t*)"00001011", (uint8_t*)"01010010", (uint8_t*)"01010011", (uint8_t*)"01010100",
|
|
(uint8_t*)"01010101", (uint8_t*)"00100100", (uint8_t*)"00100101", (uint8_t*)"01011000",
|
|
(uint8_t*)"01011001", (uint8_t*)"01011010", (uint8_t*)"01011011", (uint8_t*)"01001010",
|
|
(uint8_t*)"01001011", (uint8_t*)"00110010", (uint8_t*)"00110011", (uint8_t*)"00110100",
|
|
(uint8_t*)"11011", (uint8_t*)"10010", (uint8_t*)"010111", (uint8_t*)"0110111",
|
|
(uint8_t*)"00110110", (uint8_t*)"00110111", (uint8_t*)"01100100", (uint8_t*)"01100101",
|
|
(uint8_t*)"01101000", (uint8_t*)"01100111", (uint8_t*)"011001100",(uint8_t*)"011001101",
|
|
(uint8_t*)"011010010", (uint8_t*)"011010011",(uint8_t*)"011010100"
|
|
};
|
|
|
|
const uint8_t* zero_run_len_code_list[MAX_CDWDTBL_LEN] = {
|
|
(uint8_t*)"0000110111", (uint8_t*)"10", (uint8_t*)"11", (uint8_t*)"010",
|
|
(uint8_t*)"011", (uint8_t*)"0011", (uint8_t*)"0010", (uint8_t*)"00011",
|
|
(uint8_t*)"000101", (uint8_t*)"000100", (uint8_t*)"0000100", (uint8_t*)"0000101",
|
|
(uint8_t*)"0000111", (uint8_t*)"00000100", (uint8_t*)"00000111", (uint8_t*)"000011000",
|
|
(uint8_t*)"0000010111", (uint8_t*)"0000011000", (uint8_t*)"0000001000", (uint8_t*)"00001100111",
|
|
(uint8_t*)"00001101000", (uint8_t*)"00001101100", (uint8_t*)"00000110111", (uint8_t*)"00000101000",
|
|
(uint8_t*)"00000010111", (uint8_t*)"00000011000", (uint8_t*)"000011001010",(uint8_t*)"000011001011",
|
|
(uint8_t*)"000011001100",(uint8_t*)"000011001101",(uint8_t*)"000001101000",(uint8_t*)"000001101001",
|
|
(uint8_t*)"000001101010",(uint8_t*)"000001101011",(uint8_t*)"000011010010",(uint8_t*)"000011010011",
|
|
(uint8_t*)"000011010100",(uint8_t*)"000011010101",(uint8_t*)"000011010110",(uint8_t*)"000011010111",
|
|
(uint8_t*)"000001101100",(uint8_t*)"000001101101",(uint8_t*)"000011011010",(uint8_t*)"000011011011",
|
|
(uint8_t*)"000001010100",(uint8_t*)"000001010101",(uint8_t*)"000001010110",(uint8_t*)"000001010111",
|
|
(uint8_t*)"000001100100",(uint8_t*)"000001100101",(uint8_t*)"000001010010",(uint8_t*)"000001010011",
|
|
(uint8_t*)"000000100100",(uint8_t*)"000000110111",(uint8_t*)"000000111000",(uint8_t*)"000000100111",
|
|
(uint8_t*)"000000101000",(uint8_t*)"000001011000",(uint8_t*)"000001011001",(uint8_t*)"000000101011",
|
|
(uint8_t*)"000000101100",(uint8_t*)"000001011010",(uint8_t*)"000001100110",(uint8_t*)"000001100111",
|
|
(uint8_t*)"0000001111", (uint8_t*)"000011001000",(uint8_t*)"000011001001",(uint8_t*)"000001011011",
|
|
(uint8_t*)"000000110011",(uint8_t*)"000000110100",(uint8_t*)"000000110101",(uint8_t*)"0000001101100",
|
|
(uint8_t*)"0000001101101",(uint8_t*)"0000001001010", (uint8_t*)"0000001001011",
|
|
(uint8_t*)"0000001001100", (uint8_t*)"0000001001101", (uint8_t*)"0000001110010",
|
|
(uint8_t*)"0000001110011"
|
|
};
|
|
/*
|
|
*
|
|
*
|
|
* Desc: This function rebuilds a header from Type II to Type III
|
|
* the source is header2 and the new header is put
|
|
* into the enc structure
|
|
*
|
|
*/
|
|
|
|
int gprs_rlc_data::egprs_build_header_type3_from_type2
|
|
(
|
|
uint8_t *enc, /* header to unpack */
|
|
GprsCodingScheme *reseg_mcs, /* resegmentation MCS */
|
|
uint8_t ps,
|
|
uint8_t spb /* SPB field */
|
|
)
|
|
{
|
|
uint8_t byte;
|
|
|
|
/* Type 3 and type 2 are differed with only last byte to include spb offset */
|
|
memcpy(enc, this->hdr_ptr[0], EGPRS_TYPE3_HDR_SIZE);
|
|
|
|
struct gprs_rlc_dl_header_egprs_3 *header_type3 = (struct gprs_rlc_dl_header_egprs_3 *)enc;
|
|
|
|
byte = reseg_mcs->get_cps(ps, this->mcs8_retx);
|
|
|
|
header_type3->cps = byte;
|
|
header_type3->spb = spb;
|
|
return(1);
|
|
} /* end of egprs_build_header_type3_from_type2*/
|
|
|
|
/*
|
|
*
|
|
* Fun: egprs_build_header_type3_from_type1
|
|
*
|
|
* Desc: This function rebuilds a header from Type I to Type III
|
|
* the source is header1 and the new header is put
|
|
* into the enc structure
|
|
* This function is needed for resegmentation from MCS7/8/9 to
|
|
* MCS 2/3
|
|
*
|
|
* Ret: 1 if successful or,
|
|
* -1, otherwise
|
|
*
|
|
* Notes: None
|
|
*
|
|
* File: gz_egprs1.c
|
|
*
|
|
*/
|
|
|
|
int gprs_rlc_data::egprs_build_header_type3_from_type1
|
|
(
|
|
uint8_t *enc, /* header to unpack */
|
|
GprsCodingScheme *reseg_mcs, /* resegmentation MCS */
|
|
uint8_t ps,
|
|
uint8_t spb/* SPB field */
|
|
)
|
|
{
|
|
uint8_t byte;
|
|
|
|
struct gprs_rlc_dl_header_egprs_3 *header_type3 = (struct gprs_rlc_dl_header_egprs_3 *) enc;
|
|
|
|
memcpy(enc, this->hdr_ptr[0], EGPRS_TYPE3_HDR_SIZE);
|
|
|
|
/* depending on wether if it is the first or the second
|
|
block of the original MCS-7/8/9 block we choose the BSN
|
|
*/
|
|
|
|
header_type3->bsn_a = (this->bsn & 0x3);
|
|
header_type3->bsn_b = this->bsn >> 2 ;
|
|
header_type3->bsn_c = this->bsn & 0x0400 ;
|
|
|
|
byte = reseg_mcs->get_cps(ps, this->mcs8_retx);
|
|
|
|
header_type3->cps = byte;
|
|
header_type3->spb = spb;
|
|
|
|
return(1);
|
|
} /* end of egprs_build_header_type3_from_type1 */
|
|
|
|
|
|
/*
|
|
*
|
|
* Desc: This function rebuilds a header from Type I to Type II
|
|
* the source is header1 and the new header is put
|
|
* into the enc structure
|
|
* In this case 2 headers are returned since the BSN for
|
|
* each resegmented block is different
|
|
*/
|
|
|
|
int gprs_rlc_data::egprs_build_header_type2_from_type1
|
|
(
|
|
uint8_t *enc, /* header to unpack */
|
|
GprsCodingScheme *reseg_mcs, /* resegmentation MCS */
|
|
uint8_t ps
|
|
)
|
|
{
|
|
uint8_t byte;
|
|
|
|
struct gprs_rlc_dl_header_egprs_2 *header_type2 = (struct gprs_rlc_dl_header_egprs_2 *) enc;
|
|
|
|
memcpy(enc, this->hdr_ptr[0], EGPRS_TYPE2_HDR_SIZE);
|
|
|
|
/* depending on wether if it is the first or the second
|
|
block of the original MCS-7/8/9 block we choose the BSN
|
|
*/
|
|
header_type2->bsn_a = (this->bsn & 0x3);
|
|
header_type2->bsn_b = this->bsn >> 2 ;
|
|
header_type2->bsn_c = this->bsn & 0x0400 ;
|
|
|
|
byte = reseg_mcs->get_cps(ps, this->mcs8_retx);
|
|
|
|
header_type2->cps = byte;
|
|
|
|
return(1);
|
|
} /* end of egprs_build_header_type2_from_type1*/
|
|
|
|
void gprs_rlc_data::update_cps( GprsCodingScheme *cs, const uint8_t next_ps)
|
|
{
|
|
uint8_t cps = cs->get_cps(next_ps, false);
|
|
if((*cs == GprsCodingScheme::MCS1 ||
|
|
*cs == GprsCodingScheme::MCS2 ||
|
|
*cs == GprsCodingScheme::MCS3 ||
|
|
*cs == GprsCodingScheme::MCS4)){
|
|
|
|
this->last_ps = next_ps;
|
|
this->hdr_ptr[0][3] |= cps << 1 ;
|
|
}
|
|
else if( (*cs == GprsCodingScheme::MCS5) ||
|
|
(*cs == GprsCodingScheme::MCS6)){
|
|
|
|
this->last_ps = next_ps;
|
|
this->hdr_ptr[0][3] |= cps << 1 ;
|
|
|
|
}else if((*cs == GprsCodingScheme::MCS7) ||
|
|
(*cs == GprsCodingScheme::MCS8) ||
|
|
( *cs == GprsCodingScheme::MCS9)){
|
|
|
|
this->last_ps = next_ps;
|
|
this->hdr_ptr[0][4] = cps<< 3 ;
|
|
}
|
|
else{
|
|
OSMO_ASSERT(*cs >= GprsCodingScheme::MCS9);
|
|
}
|
|
}
|
|
|
|
void gprs_rlc_data::fill_hdr_type3( GprsCodingScheme *cs, const uint16_t bsn, const uint16_t tfi,const uint16_t cps)
|
|
{
|
|
/*Refer Header type from 10.3a.3.3 of 46.060*/
|
|
struct gprs_rlc_dl_header_egprs_3 *ptr = (struct gprs_rlc_dl_header_egprs_3 *)hdr_ptr[0];
|
|
|
|
ptr->usf = GPRS_RLCMAC_DATA_BLOCK;
|
|
ptr->tfi_a = tfi & 0x01;
|
|
|
|
ptr->tfi_b = tfi >> 1;
|
|
ptr->bsn_a = bsn & 0x3;
|
|
|
|
ptr->bsn_b = bsn >> 2 ;
|
|
|
|
ptr->bsn_c = bsn & 0x0400 ;
|
|
|
|
/* Need to check with cs */
|
|
|
|
ptr->cps = cps;
|
|
ptr->spb = 0;
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "usf=%d\n tfi_a=%d\n tfi_b=%d\n bsna %d \n bsnb %d\n bsnc %d\n cps %d\n spb %d\n",
|
|
ptr->usf, ptr->tfi_a, ptr->tfi_b, ptr->bsn_a,ptr->bsn_b,ptr->bsn_c,ptr->cps,ptr->spb);
|
|
|
|
|
|
this->cs = *cs;
|
|
this->hdr_size[0] = EGPRS_TYPE3_HDR_SIZE;
|
|
}
|
|
|
|
void gprs_rlc_data::fill_hdr_type2( GprsCodingScheme *cs, const uint16_t bsn, const uint16_t tfi,const uint16_t cps )
|
|
{
|
|
/*Refer Header type from 10.3a.3.2 of 46.060*/
|
|
struct gprs_rlc_dl_header_egprs_2 *ptr = (struct gprs_rlc_dl_header_egprs_2 *)hdr_ptr[0];
|
|
ptr->usf = GPRS_RLCMAC_DATA_BLOCK;
|
|
|
|
ptr->tfi_a = tfi & 0x01;
|
|
|
|
ptr->tfi_b = tfi >> 1;
|
|
ptr->bsn_a = bsn & 0x3;
|
|
|
|
ptr->bsn_b = bsn >> 2 ;
|
|
|
|
ptr->bsn_c = bsn & 0x0400 ;
|
|
|
|
/* Need to check with cs */
|
|
|
|
ptr->cps = cps;
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "usf=%d\n tfi_a=%d\n tfi_b=%d\n bsna %d \n bsnb %d\n bsnc %d\n cps %d\n",
|
|
ptr->usf, ptr->tfi_a, ptr->tfi_b, ptr->bsn_a,ptr->bsn_b,ptr->bsn_c,ptr->cps);
|
|
|
|
this->cs = *cs;
|
|
this->hdr_size[0] = EGPRS_TYPE2_HDR_SIZE;
|
|
}
|
|
|
|
void gprs_rlc_data::set_resend_fn_ts(const uint32_t fn, const uint8_t ts)
|
|
{
|
|
this->resend_fn = fn;
|
|
this->resend_ts = ts;
|
|
}
|
|
|
|
void gprs_rlc_data::fill_hdr_type1(GprsCodingScheme *cs, const uint16_t bsn, const uint16_t tfi , const uint16_t cps)
|
|
{
|
|
/*Refer Header type from 10.3a.3.1 of 46.060*/
|
|
struct gprs_rlc_dl_header_egprs_1 *ptr = (struct gprs_rlc_dl_header_egprs_1 *)hdr_ptr[0];
|
|
ptr->usf = GPRS_RLCMAC_DATA_BLOCK;
|
|
|
|
ptr->tfi_a = tfi & 0x01;
|
|
|
|
ptr->tfi_b = tfi >> 1;
|
|
ptr->bsn1_a = bsn & 0x3;
|
|
|
|
ptr->bsn1_b = bsn >> 2 ;
|
|
|
|
ptr->bsn1_c = bsn & 0x0400 ;
|
|
/*
|
|
Theres no scope of retx and Tx block clubbing present in our design
|
|
hence bsn2 is always assumed to be 1 i.e bsn2 = nextbsn(bsn1) = 1
|
|
*/
|
|
ptr->bsn2_a = 1;
|
|
ptr->bsn2_b = 0 ;
|
|
|
|
ptr->cps = cps;
|
|
this->cs = *cs;
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "usf :%d \n es :%d\n rrbp: %d \n tfi1:%d\n tfi2:%d\n bsn1a%d \n bsn1b %d \n bsn1c%d\n"
|
|
"bsn2a %d\n bsn2b %d\n ",ptr ->usf, ptr -> es_p, ptr -> rrbp, ptr -> tfi_a, ptr ->tfi_b, ptr->bsn1_a,
|
|
ptr->bsn1_b, ptr->bsn1_c, ptr->bsn2_a, ptr->bsn2_b);
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "pr: %d cps:%d ", ptr -> pr, ptr ->cps);
|
|
|
|
this->hdr_size[0] = EGPRS_TYPE1_HDR_SIZE;
|
|
}
|
|
|
|
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(complete_blk, 0x0, sizeof(complete_blk));
|
|
memset(complete_blk, 0x2b, block_data_len);
|
|
memset(&hdr_ptr[0],0, RLC_MAX_HDR_SIZE);
|
|
memset(&hdr_ptr[1],0, RLC_MAX_HDR_SIZE);
|
|
|
|
this->mcs8_retx = false;
|
|
this->reseg_status = EGPRS_RESEG_SINGLE_BLOCK;
|
|
this->len_block2 = 0;
|
|
|
|
return complete_blk;
|
|
}
|
|
|
|
void gprs_rlc_data::put_data(const uint8_t *data, size_t data_len)
|
|
{
|
|
memcpy(complete_blk, data, data_len);
|
|
completed_block_len = data_len;
|
|
}
|
|
|
|
void gprs_rlc_v_b::reset()
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
|
|
mark_invalid(i);
|
|
}
|
|
|
|
void gprs_rlc_dl_window::reset()
|
|
{
|
|
m_v_s = 0;
|
|
m_v_a = 0;
|
|
m_v_b.reset();
|
|
}
|
|
|
|
void gprs_rlc_dl_window::set_sns(uint16_t sns)
|
|
{
|
|
OSMO_ASSERT(sns >= RLC_GPRS_SNS);
|
|
OSMO_ASSERT(sns <= RLC_MAX_SNS);
|
|
/* check for 2^n */
|
|
OSMO_ASSERT((sns & (-sns)) == sns);
|
|
m_sns = sns;
|
|
}
|
|
|
|
void gprs_rlc_dl_window::set_ws(uint16_t ws)
|
|
{
|
|
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
|
|
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
|
|
m_ws = ws;
|
|
}
|
|
|
|
int gprs_rlc_dl_window::resend_needed()
|
|
{
|
|
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = this->mod_sns(bsn + 1)) {
|
|
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 = this->mod_sns(bsn + 1)) {
|
|
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 = this->mod_sns(bsn + 1)) {
|
|
if (!m_v_b.is_acked(bsn))
|
|
unacked += 1;
|
|
}
|
|
|
|
return unacked;
|
|
}
|
|
|
|
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
|
|
{
|
|
return (ssn - 1 - bitnum);
|
|
}
|
|
|
|
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_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 = this->mod_sns(bitnum_to_bsn(bitpos, ssn));
|
|
|
|
if (bsn == this->mod_sns(v_a() - 1))
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void extract_egprs_crbb_urbb(uint16_t uncomp_length, const uint8_t *uncomp_bitmap, char *show_rbb)
|
|
{
|
|
for (int i = 0; i < uncomp_length; i++) {
|
|
uint8_t bit;
|
|
|
|
bit = !!(uncomp_bitmap[i/8] & (1<<(7-i%8)));
|
|
show_rbb[i] = bit ? 'R' : 'I';
|
|
}
|
|
|
|
show_rbb[uncomp_length] = '\0';
|
|
}
|
|
|
|
void extract_egprs_urbb(uint16_t uncomp_length, const uint8_t *uncomp_bitmap, char *show_rbb,uint16_t start_index)
|
|
{
|
|
for (int i = 0; i < uncomp_length; i++) {
|
|
uint8_t bit;
|
|
|
|
bit = !!(uncomp_bitmap[i/8] & (1<<(7-i%8)));
|
|
show_rbb[start_index+uncomp_length-i] = bit ? 'R' : 'I';
|
|
}
|
|
|
|
show_rbb[uncomp_length] = '\0';
|
|
}
|
|
|
|
int search_runlen(
|
|
Node *root, /* root of Ones or Zeros tree */
|
|
uint8_t* bmbuf, /* Received compressed bitmap buf */
|
|
uint8_t bit_pos, /* the start bit pos to read codeword */
|
|
uint8_t* len_codewd, /* length of codeword */
|
|
uint16_t* rlen /* run length of Ones or Zeros */
|
|
)
|
|
{
|
|
Node* iter;
|
|
uint8_t dir;
|
|
|
|
iter = root;
|
|
*len_codewd = 0;
|
|
|
|
while (iter->run_length == 0) {
|
|
if ((iter->left == NULL)&& (iter->right == NULL))
|
|
return -1;
|
|
|
|
/* get the bit value at the bitpos and put it in right most of dir */
|
|
dir = (bmbuf[BITS_TO_BYTES(bit_pos)-1]>>(7-(MOD8(bit_pos))))&0x01;
|
|
(bit_pos)++;
|
|
(*len_codewd)++;
|
|
|
|
if (((dir&0x01) == 0) && (iter->left != NULL))
|
|
iter = iter->left;
|
|
|
|
else if (((dir&0x01) == 1) && (iter->right != NULL))
|
|
iter = iter->right;
|
|
else
|
|
return -1;
|
|
}
|
|
(*rlen) = *(iter->run_length);
|
|
|
|
return 1;
|
|
} /* search_runlen */
|
|
|
|
void gprs_rlc_dl_window::decompress_crbb(
|
|
BTS *bts,
|
|
int8_t compress_bmap_len, /* compressed bitmap length */
|
|
uint8_t clr_code_bit, /* run length of Ones or Zeros */
|
|
uint8_t* orig_buf, /* received block bitmap */
|
|
int16_t orig_bmaplen, /* bitmap length of Ack/Nack descr */
|
|
uint16_t* decompress_bmap_len, /* total bitmap length after decompression */
|
|
uint8_t* uncompress_bmbuf, /* bitmap after decompression */
|
|
bitvec *dest
|
|
)
|
|
{
|
|
uint8_t bit_pos = 0;
|
|
uint8_t nbits = 0; /* number of bits of codeword */
|
|
uint16_t run_length = 0;
|
|
uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
|
|
unsigned wp = 0;
|
|
*decompress_bmap_len = 0;
|
|
int8_t Lc = compress_bmap_len;
|
|
|
|
while (compress_bmap_len >= 0) {
|
|
|
|
if (clr_code_bit == 1) {
|
|
search_runlen (bts->ones_list, orig_buf, bit_pos, &nbits, &run_length);
|
|
//If run length > 64, need makeup and terminating code
|
|
if (run_length < 64) {
|
|
clr_code_bit = 0;
|
|
}
|
|
cbmaplen= cbmaplen + run_length;
|
|
/* put run length of Ones in uncompressed bitmap */
|
|
while (run_length !=0) {
|
|
if (run_length > 8) {
|
|
bitvec_write_field(dest, wp, 0xff, 8);
|
|
run_length = run_length -8;
|
|
}
|
|
else {
|
|
bitvec_write_field(dest, wp, 0xff, run_length);
|
|
run_length = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
search_runlen (bts->zeros_list, orig_buf, bit_pos, &nbits, &run_length);
|
|
//If run length > 64, need makeup and terminating code
|
|
if (run_length < 64) {
|
|
clr_code_bit = 1;
|
|
}
|
|
cbmaplen= cbmaplen + run_length;
|
|
/* put run length of Zeros in uncompressed bitmap */
|
|
while (run_length !=0) {
|
|
if (run_length > 8) {
|
|
bitvec_write_field(dest, wp, 0x00, 8);
|
|
run_length = run_length -8;
|
|
}
|
|
else {
|
|
bitvec_write_field(dest, wp, 0x00, run_length);
|
|
run_length = 0;
|
|
}
|
|
}
|
|
}
|
|
bit_pos = bit_pos + nbits;
|
|
compress_bmap_len = compress_bmap_len - nbits;
|
|
}
|
|
|
|
/* Decompress_bmap_len is the len of uncompressed bitmap
|
|
+ Original bitmap len of ack/nack desc
|
|
- Original compressed bitmap len
|
|
==> this is requird in extract rbb func*/
|
|
(*decompress_bmap_len) = cbmaplen + orig_bmaplen - Lc - 23;
|
|
|
|
return ;
|
|
}/* Decompress_CRBB */
|
|
|
|
void gprs_rlc_dl_window::egprs_update(BTS *bts, char *show_rbb,
|
|
EGPRS_AckNack_Desc_t *Egprs_Desc, uint16_t *lost,
|
|
uint16_t *received, int16_t len)
|
|
{
|
|
int16_t ssn = Egprs_Desc->STARTING_SEQUENCE_NUMBER;
|
|
uint8_t bow = Egprs_Desc->BEGINNING_OF_WINDOW;
|
|
uint8_t eow = Egprs_Desc->END_OF_WINDOW;
|
|
uint16_t uncomp_len = 0; /* total bitmap length of compressed part after
|
|
decompcompress plus uncompressed part */
|
|
uint8_t uncomp_bitmap[128];
|
|
uint16_t crbb_urbb_len = 0; /* bitmap length of compressed part after decompression */
|
|
uint16_t urbb_len;
|
|
int8_t Lc = Egprs_Desc->CRBB_LENGTH;
|
|
|
|
bitvec dest;
|
|
dest.data = uncomp_bitmap;
|
|
dest.cur_bit = 0;
|
|
dest.data_len = 128;
|
|
|
|
if(len > 0) {
|
|
urbb_len = len - Lc;
|
|
}
|
|
else {
|
|
urbb_len = Egprs_Desc->URBB_LENGTH;
|
|
}
|
|
if(Egprs_Desc->Exist_CRBB) {
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exist, "
|
|
"CRBB LEN =%d and Starting color code =%d",
|
|
Lc, Egprs_Desc->CRBB_STARTING_COLOR_CODE);
|
|
|
|
decompress_crbb(bts,
|
|
Lc,
|
|
Egprs_Desc->CRBB_STARTING_COLOR_CODE,
|
|
Egprs_Desc->CRBB,
|
|
len, //Ack Nack desc length
|
|
&uncomp_len,
|
|
uncomp_bitmap,
|
|
&dest);
|
|
|
|
/* now attach unCompressed bitmap part to the decompressed bitmap
|
|
* in unCompBitmap array if there is any */
|
|
/* if (Lc != 0) {
|
|
ubmaplen = acknack_len - Lc - 23;
|
|
}
|
|
else {
|
|
ubmaplen = acknack_len - 15;
|
|
}*/
|
|
crbb_urbb_len = dest.cur_bit;
|
|
extract_egprs_crbb_urbb(crbb_urbb_len, uncomp_bitmap, show_rbb);
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "crbb len %d uncomp len %d\n",Lc,crbb_urbb_len);
|
|
}
|
|
|
|
/* uncompressed part */
|
|
if ((urbb_len !=0) && (Egprs_Desc->URBB != (uint8_t *)NULL)) {
|
|
extract_egprs_urbb(urbb_len, Egprs_Desc->URBB, show_rbb, crbb_urbb_len);
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "urbb len %d\n" ,urbb_len);
|
|
}
|
|
|
|
/* if Beginning of window is set 1, Mark Acked between SSN-2 ( V(Q)-1) to V(A)*/
|
|
if (bow) {
|
|
for(int i = (v_a() -1); i < (ssn -2); i++ ) {
|
|
uint16_t bsn = this->mod_sns(i);
|
|
*received += 1;
|
|
m_v_b.mark_acked(bsn);
|
|
}
|
|
/* ssn -1 which is V(Q) should be nacked */
|
|
uint16_t bsn = ((2048 + (ssn-1)) & 0x07FF);/* MOD 2048 (MAX BSN NUM) */
|
|
m_v_b.mark_nacked(bsn);
|
|
bts->rlc_nacked();
|
|
*lost += 1;
|
|
}
|
|
|
|
/* SSN - 1 is in range V(A)..V(S)-1 */
|
|
for (int bitpos = 0; bitpos < crbb_urbb_len + urbb_len; bitpos++) {
|
|
uint16_t bsn = mod_sns(ssn + bitpos);
|
|
|
|
if (mod_sns(v_s() -bsn) >= distance())
|
|
break;
|
|
|
|
if (show_rbb[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;
|
|
}
|
|
}
|
|
|
|
/* if EOW is set, nack data btw last BSN indicated in the bitmap and
|
|
* the end of tx window V(S) */
|
|
if(eow) {
|
|
/* Currently EOW is not handled
|
|
for (int i = ssn + crbb_urbb_len + uncomp_len; bsn < v_s(); i++)
|
|
uint16_t bsn = mod_sns(ssn + i);
|
|
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 = this->mod_sns(bsn + 1)) {
|
|
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 = this->mod_sns(bsn + 1)) {
|
|
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;
|
|
}
|
|
|
|
void gprs_rlc_ul_window::set_sns(uint16_t sns)
|
|
{
|
|
OSMO_ASSERT(sns >= RLC_GPRS_SNS);
|
|
OSMO_ASSERT(sns <= RLC_MAX_SNS);
|
|
/* check for 2^n */
|
|
OSMO_ASSERT((sns & (-sns)) == sns);
|
|
m_sns = sns;
|
|
}
|
|
|
|
void gprs_rlc_ul_window::set_ws(uint16_t ws)
|
|
{
|
|
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
|
|
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
|
|
m_ws = ws;
|
|
}
|
|
|
|
/* 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';
|
|
}
|
|
}
|
|
|
|
/* Update the receive block bitmap */
|
|
uint16_t gprs_rlc_ul_window::update_egprs_rbb(char *rbb)
|
|
{
|
|
int i;
|
|
uint16_t bsn;
|
|
for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++, bsn = this->mod_sns(bsn + 1)) {
|
|
//if (m_v_n.is_received(ssn()-1-i))
|
|
if (m_v_n.is_received(bsn)) //bsn
|
|
//rbb[ws()-1-i] = 'R';
|
|
rbb[i] = 'R'; //i
|
|
else
|
|
rbb[i] = 'I';
|
|
}
|
|
LOGP(DRLCMACUL, LOGL_DEBUG, "V(N):in update_egprs_rbb \"%s\" R=Received "
|
|
"I=Invalid\n", rbb);
|
|
return 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 = this->mod_sns(bsn + 1 - v_r());
|
|
/* 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(), this->mod_sns(v_q() + 1));
|
|
raise_v_q(1);
|
|
count += 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
|
|
{
|
|
m_v_n.mark_received(bsn);
|
|
raise_v_r(bsn);
|
|
}
|
|
|
|
bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
|
|
{
|
|
bool was_valid = m_v_n.is_received(bsn);
|
|
m_v_n.mark_missing(bsn);
|
|
|
|
return was_valid;
|
|
}
|