| /* 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 <egprs_rlc_compression.h> |
| |
| extern "C" { |
| #include <osmocom/core/utils.h> |
| #include <osmocom/core/bitcomp.h> |
| #include <osmocom/gprs/protocol/gsm_04_60.h> |
| } |
| |
| #include <arpa/inet.h> |
| |
| #include <errno.h> |
| #include <string.h> |
| |
| #define LENGTH_TO_END 255 |
| /*! |
| * \returns num extensions fields (num frames == offset) on success, |
| * -errno otherwise. |
| */ |
| static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len, |
| unsigned int *offs, |
| bool is_last_block, |
| Decoding::RlcData *chunks, unsigned int chunks_size) |
| { |
| const struct rlc_li_field_egprs *li; |
| uint8_t e; |
| unsigned int num_chunks = 0; |
| |
| e = 0; |
| while (!e) { |
| if (*offs > data_len) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " |
| "but no more data\n"); |
| return -EINVAL; |
| } |
| |
| /* get new E */ |
| li = (struct rlc_li_field_egprs *)&data[*offs]; |
| e = li->e; |
| *offs += 1; |
| |
| if (!chunks) |
| continue; |
| |
| if (num_chunks == chunks_size) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " |
| "but no more chunks possible\n"); |
| return -ENOSPC; |
| } |
| if (li->li == 0 && num_chunks == 0) { |
| /* TS 44.060, table 10.4.14a.1, row 2a */ |
| /* TS 44.060, table 10.4.14a.1, row 4 */ |
| chunks[num_chunks].length = 0; |
| chunks[num_chunks].is_complete = true; |
| } else if (li->li == 127 && li->e == 1) { |
| /* TS 44.060, table 10.4.14a.1, row 3 & 5 */ |
| /* only filling bytes left */ |
| LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains " |
| "only filling bytes with extension octet: LI=%d, E=%d, count=%d\n", |
| li->li, li->e, num_chunks); |
| break; |
| } else if (li->li > 0) { |
| /* TS 44.060, table 10.4.14a.1, row 1 & 2b */ |
| chunks[num_chunks].length = li->li; |
| chunks[num_chunks].is_complete = true; |
| } else { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains " |
| "invalid extension octet: LI=%d, E=%d, count=%d\n", |
| li->li, li->e, num_chunks); |
| return -EINVAL; |
| } |
| |
| LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains " |
| "extension octet: LI=%d, E=%d, count=%d\n", |
| li->li, li->e, num_chunks); |
| num_chunks += 1; |
| |
| if (e == 1) { |
| /* There is space after the last chunk, add a final one */ |
| if (num_chunks == chunks_size) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, |
| "UL DATA LI possibly extended, " |
| "but no more chunks possible\n"); |
| return -ENOSPC; |
| } |
| |
| chunks[num_chunks].length = LENGTH_TO_END; |
| chunks[num_chunks].is_complete = is_last_block; |
| num_chunks += 1; |
| } |
| } |
| |
| return num_chunks; |
| } |
| |
| static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len, |
| unsigned int *offs, |
| bool is_last_block, |
| Decoding::RlcData *chunks, unsigned int chunks_size) |
| { |
| const struct rlc_li_field *li; |
| uint8_t m, e; |
| unsigned int num_chunks = 0; |
| |
| e = 0; |
| while (!e) { |
| if (*offs > data_len) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " |
| "but no more data\n"); |
| return -EINVAL; |
| } |
| |
| /* get new E */ |
| li = (const struct rlc_li_field *)&data[*offs]; |
| e = li->e; |
| m = li->m; |
| *offs += 1; |
| |
| if (li->li == 0) { |
| /* TS 44.060, 10.4.14, par 6 */ |
| e = 1; |
| m = 0; |
| } |
| |
| /* TS 44.060, table 10.4.13.1 */ |
| if (m == 0 && e == 0) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA " |
| "ignored, because M='0' and E='0'.\n"); |
| return 0; |
| } |
| |
| if (!chunks) |
| continue; |
| |
| if (num_chunks == chunks_size) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " |
| "but no more chunks possible\n"); |
| return -ENOSPC; |
| } |
| |
| if (li->li == 0) |
| /* e is 1 here */ |
| chunks[num_chunks].length = LENGTH_TO_END; |
| else |
| chunks[num_chunks].length = li->li; |
| |
| chunks[num_chunks].is_complete = li->li || is_last_block; |
| |
| LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains " |
| "extension octet: LI=%d, M=%d, E=%d, count=%d\n", |
| li->li, li->m, li->e, num_chunks); |
| num_chunks += 1; |
| |
| if (e == 1 && m == 1) { |
| if (num_chunks == chunks_size) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, " |
| "but no more chunks possible\n"); |
| return -ENOSPC; |
| } |
| /* TS 44.060, 10.4.13.1, row 4 */ |
| chunks[num_chunks].length = LENGTH_TO_END; |
| chunks[num_chunks].is_complete = is_last_block; |
| num_chunks += 1; |
| } |
| } |
| |
| return num_chunks; |
| } |
| |
| int Decoding::rlc_data_from_ul_data( |
| const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs, |
| const uint8_t *data, RlcData *chunks, unsigned int chunks_size, |
| uint32_t *tlli) |
| { |
| uint8_t e; |
| unsigned int data_len = rdbi->data_len; |
| int num_chunks = 0, i; |
| unsigned int offs = 0; |
| bool is_last_block = (rdbi->cv == 0); |
| |
| if (!chunks) |
| chunks_size = 0; |
| |
| e = rdbi->e; |
| if (e) { |
| if (chunks_size > 0) { |
| /* Block without LI means it only contains data of one LLC PDU */ |
| chunks[num_chunks].offset = offs; |
| chunks[num_chunks].length = LENGTH_TO_END; |
| chunks[num_chunks].is_complete = is_last_block; |
| num_chunks += 1; |
| } else if (chunks) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, " |
| "but no more chunks possible\n"); |
| return -ENOSPC; |
| } |
| } else if (mcs_is_edge(cs)) { |
| /* if E is not set (LI follows), EGPRS */ |
| num_chunks = parse_extensions_egprs(data, data_len, &offs, |
| is_last_block, |
| chunks, chunks_size); |
| } else { |
| /* if E is not set (LI follows), GPRS */ |
| num_chunks = parse_extensions_gprs(data, data_len, &offs, |
| is_last_block, |
| chunks, chunks_size); |
| } |
| |
| if (num_chunks < 0) |
| return num_chunks; |
| |
| /* TLLI */ |
| if (rdbi->ti) { |
| uint32_t tlli_enc; |
| if (offs + 4 > data_len) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block " |
| "border\n"); |
| return -EINVAL; |
| } |
| |
| memcpy(&tlli_enc, data + offs, sizeof(tlli_enc)); |
| if (mcs_is_gprs(cs)) |
| /* The TLLI is encoded in big endian for GPRS (see |
| * TS 44.060, figure 10.2.2.1, note) */ |
| *tlli = be32toh(tlli_enc); |
| else |
| /* The TLLI is encoded in little endian for EGPRS (see |
| * TS 44.060, figure 10.3a.2.1, note 2) */ |
| *tlli = le32toh(tlli_enc); |
| |
| offs += sizeof(tlli_enc); |
| } else { |
| *tlli = 0; |
| } |
| |
| /* PFI */ |
| if (rdbi->pi) { |
| LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, " |
| "please disable in SYSTEM INFORMATION\n"); |
| return -ENOTSUP; |
| |
| /* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */ |
| } |
| |
| if (chunks_size == 0) |
| return num_chunks; |
| |
| /* LLC */ |
| for (i = 0; i < num_chunks; i++) { |
| chunks[i].offset = offs; |
| if (chunks[i].length == LENGTH_TO_END) { |
| if (offs == data_len) { |
| /* There is no place for an additional chunk, |
| * so drop it (this may happen with EGPRS since |
| * there is no M flag. */ |
| num_chunks -= 1; |
| break; |
| } |
| chunks[i].length = data_len - offs; |
| } |
| offs += chunks[i].length; |
| if (offs > data_len) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block " |
| "border, chunk idx: %d, size: %d\n", |
| i, chunks[i].length); |
| return -EINVAL; |
| } |
| } |
| |
| return num_chunks; |
| } |
| |
| 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; |
| } |
| |
| uint8_t Decoding::get_egprs_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_EGPRS_multislot_class) |
| continue; |
| return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_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'; |
| } |
| |
| void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb) |
| { |
| unsigned int i; |
| for (i = 0; i < rbb->cur_bit; i++) { |
| uint8_t bit; |
| bit = bitvec_get_bit_pos(rbb, i); |
| show_rbb[i] = bit == 1 ? 'R' : 'I'; |
| } |
| |
| show_rbb[i] = '\0'; |
| } |
| |
| int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc, |
| const uint8_t *data, GprsCodingScheme cs) |
| { |
| unsigned int cur_bit = 0; |
| switch(cs.headerTypeData()) { |
| case HEADER_GPRS_DATA : |
| cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs); |
| break; |
| case HEADER_EGPRS_DATA_TYPE_3 : |
| cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs); |
| break; |
| case HEADER_EGPRS_DATA_TYPE_2 : |
| cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs); |
| break; |
| case HEADER_EGPRS_DATA_TYPE_1 : |
| cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs); |
| break; |
| default: |
| LOGP(DRLCMACDL, LOGL_ERROR, |
| "Decoding of uplink %s data blocks not yet supported.\n", |
| mcs_name(cs)); |
| return -ENOTSUP; |
| }; |
| |
| return cur_bit; |
| } |
| |
| int Decoding::rlc_parse_ul_data_header_egprs_type_3( |
| struct gprs_rlc_data_info *rlc, |
| const uint8_t *data, |
| const GprsCodingScheme &cs) |
| { |
| int punct, punct2, with_padding, cps; |
| unsigned int e_ti_header, offs, cur_bit = 0; |
| const struct gprs_rlc_ul_header_egprs_3 *egprs3; |
| |
| egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * > |
| ((void *)data); |
| |
| cps = (egprs3->cps_hi << 0) | (egprs3->cps_lo << 2); |
| gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding); |
| gprs_rlc_data_info_init_ul(rlc, cs, with_padding); |
| |
| rlc->r = egprs3->r; |
| rlc->si = egprs3->si; |
| rlc->tfi = (egprs3->tfi_hi << 0) | (egprs3->tfi_lo << 2); |
| rlc->cps = cps; |
| rlc->rsb = egprs3->rsb; |
| |
| rlc->num_data_blocks = 1; |
| rlc->block_info[0].cv = egprs3->cv; |
| rlc->block_info[0].pi = egprs3->pi; |
| rlc->block_info[0].spb = egprs3->spb; |
| rlc->block_info[0].bsn = |
| (egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5); |
| |
| cur_bit += rlc->data_offs_bits[0] - 2; |
| offs = rlc->data_offs_bits[0] / 8; |
| OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1); |
| e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7; |
| rlc->block_info[0].e = !!(e_ti_header & 0x01); |
| rlc->block_info[0].ti = !!(e_ti_header & 0x02); |
| cur_bit += 2; |
| /* skip data area */ |
| cur_bit += cs.maxDataBlockBytes() * 8; |
| |
| return cur_bit; |
| } |
| |
| int Decoding::rlc_parse_ul_data_header_egprs_type_2( |
| struct gprs_rlc_data_info *rlc, |
| const uint8_t *data, |
| const GprsCodingScheme &cs) |
| { |
| const struct gprs_rlc_ul_header_egprs_2 *egprs2; |
| unsigned int e_ti_header, offs, cur_bit = 0; |
| int punct, punct2, with_padding, cps; |
| |
| egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * > |
| ((void *)data); |
| |
| cps = (egprs2->cps_hi << 0) | (egprs2->cps_lo << 2); |
| gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding); |
| gprs_rlc_data_info_init_ul(rlc, cs, with_padding); |
| |
| rlc->r = egprs2->r; |
| rlc->si = egprs2->si; |
| rlc->tfi = (egprs2->tfi_hi << 0) | (egprs2->tfi_lo << 2); |
| rlc->cps = cps; |
| rlc->rsb = egprs2->rsb; |
| |
| rlc->num_data_blocks = 1; |
| rlc->block_info[0].cv = egprs2->cv; |
| rlc->block_info[0].pi = egprs2->pi; |
| rlc->block_info[0].bsn = |
| (egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5); |
| |
| cur_bit += rlc->data_offs_bits[0] - 2; |
| |
| offs = rlc->data_offs_bits[0] / 8; |
| OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7); |
| |
| e_ti_header = (data[offs] & 0x60) >> 5; |
| rlc->block_info[0].e = !!(e_ti_header & 0x01); |
| rlc->block_info[0].ti = !!(e_ti_header & 0x02); |
| cur_bit += 2; |
| |
| /* skip data area */ |
| cur_bit += cs.maxDataBlockBytes() * 8; |
| |
| return cur_bit; |
| } |
| |
| int Decoding::rlc_parse_ul_data_header_egprs_type_1( |
| struct gprs_rlc_data_info *rlc, |
| const uint8_t *data, const GprsCodingScheme &cs) |
| { |
| struct gprs_rlc_ul_header_egprs_1 *egprs1; |
| unsigned int e_ti_header, cur_bit = 0, offs; |
| int punct, punct2, with_padding; |
| |
| egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * > |
| ((void *)data); |
| gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2, |
| &with_padding); |
| gprs_rlc_data_info_init_ul(rlc, cs, with_padding); |
| |
| rlc->r = egprs1->r; |
| rlc->si = egprs1->si; |
| rlc->tfi = (egprs1->tfi_hi << 0) | (egprs1->tfi_lo << 2); |
| rlc->cps = egprs1->cps; |
| rlc->rsb = egprs1->rsb; |
| rlc->num_data_blocks = 2; |
| rlc->block_info[0].cv = egprs1->cv; |
| rlc->block_info[0].pi = egprs1->pi; |
| rlc->block_info[0].bsn = |
| (egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5); |
| |
| cur_bit += rlc->data_offs_bits[0] - 2; |
| offs = rlc->data_offs_bits[0] / 8; |
| OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0); |
| |
| e_ti_header = data[offs - 1] >> 6; |
| rlc->block_info[0].e = (e_ti_header & 0x01); |
| rlc->block_info[0].ti = !!(e_ti_header & 0x02); |
| cur_bit += 2; |
| |
| rlc->block_info[1].cv = egprs1->cv; |
| rlc->block_info[1].pi = egprs1->pi; |
| rlc->block_info[1].bsn = rlc->block_info[0].bsn + |
| ((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2)); |
| rlc->block_info[1].bsn = rlc->block_info[1].bsn & (RLC_EGPRS_SNS - 1); |
| |
| if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) && |
| (rlc->block_info[0].cv == 0)) |
| rlc->block_info[0].cv = 1; |
| |
| cur_bit = rlc->data_offs_bits[1] - 2; |
| |
| offs = rlc->data_offs_bits[1] / 8; |
| OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2); |
| |
| e_ti_header = (data[offs] & (0x03)); |
| rlc->block_info[1].e = (e_ti_header & 0x01); |
| rlc->block_info[1].ti = !!(e_ti_header & 0x02); |
| cur_bit += 2; |
| /* skip data area */ |
| cur_bit += cs.maxDataBlockBytes() * 8; |
| |
| return cur_bit; |
| } |
| |
| int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc, |
| const uint8_t *data, const GprsCodingScheme &cs) |
| { |
| const struct rlc_ul_header *gprs; |
| unsigned int cur_bit = 0; |
| |
| gprs = static_cast < struct rlc_ul_header * > |
| ((void *)data); |
| |
| gprs_rlc_data_info_init_ul(rlc, cs, false); |
| |
| rlc->r = gprs->r; |
| rlc->si = gprs->si; |
| rlc->tfi = gprs->tfi; |
| rlc->cps = 0; |
| rlc->rsb = 0; |
| rlc->num_data_blocks = 1; |
| rlc->block_info[0].cv = gprs->cv; |
| rlc->block_info[0].pi = gprs->pi; |
| rlc->block_info[0].bsn = gprs->bsn; |
| rlc->block_info[0].e = gprs->e; |
| rlc->block_info[0].ti = gprs->ti; |
| rlc->block_info[0].spb = 0; |
| cur_bit += rlc->data_offs_bits[0]; |
| /* skip data area */ |
| cur_bit += cs.maxDataBlockBytes() * 8; |
| |
| return cur_bit; |
| } |
| |
| /** |
| * \brief Copy LSB bitstream RLC data block to byte aligned buffer. |
| * |
| * Note that the bitstream is encoded in LSB first order, so the two octets |
| * 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3 |
| * (LSB has bit position 1). This is a different order than the one used by |
| * CSN.1. |
| * |
| * \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise |
| * \param src A pointer to the start of the RLC block (incl. the header) |
| * \param buffer A data area of a least the size of the RLC block |
| * \returns the number of bytes copied |
| */ |
| unsigned int Decoding::rlc_copy_to_aligned_buffer( |
| const struct gprs_rlc_data_info *rlc, |
| unsigned int data_block_idx, |
| const uint8_t *src, uint8_t *buffer) |
| { |
| unsigned int hdr_bytes; |
| unsigned int extra_bits; |
| unsigned int i; |
| |
| uint8_t c, last_c; |
| uint8_t *dst; |
| const struct gprs_rlc_data_block_info *rdbi; |
| |
| OSMO_ASSERT(data_block_idx < rlc->num_data_blocks); |
| rdbi = &rlc->block_info[data_block_idx]; |
| |
| hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3; |
| extra_bits = (rlc->data_offs_bits[data_block_idx] & 7); |
| |
| if (extra_bits == 0) { |
| /* It is aligned already */ |
| memmove(buffer, src + hdr_bytes, rdbi->data_len); |
| return rdbi->data_len; |
| } |
| |
| dst = buffer; |
| src = src + hdr_bytes; |
| last_c = *(src++); |
| |
| for (i = 0; i < rdbi->data_len; i++) { |
| c = src[i]; |
| *(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits)); |
| last_c = c; |
| } |
| |
| return rdbi->data_len; |
| } |
| |
| /** |
| * \brief Get a pointer to byte aligned RLC data. |
| * |
| * Since the RLC data may not be byte aligned to the RLC block data such that a |
| * single RLC data byte is spread over two RLC block bytes, this function |
| * eventually uses the provided buffer as data storage. |
| * |
| * \param src A pointer to the start of the RLC block (incl. the header) |
| * \param buffer A data area of a least the size of the RLC block |
| * \returns A pointer to the RLC data start within src if it is aligned, and |
| * buffer otherwise. |
| */ |
| const uint8_t *Decoding::rlc_get_data_aligned( |
| const struct gprs_rlc_data_info *rlc, |
| unsigned int data_block_idx, |
| const uint8_t *src, uint8_t *buffer) |
| { |
| unsigned int hdr_bytes; |
| unsigned int extra_bits; |
| |
| OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits)); |
| |
| hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3; |
| extra_bits = (rlc->data_offs_bits[data_block_idx] & 7); |
| |
| if (extra_bits == 0) |
| /* It is aligned already, return a pointer that refers to the |
| * original data. */ |
| return src + hdr_bytes; |
| |
| Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer); |
| return buffer; |
| } |
| |
| static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end, |
| gprs_rlc_dl_window *window) |
| { |
| int num_blocks, i; |
| |
| num_blocks = window->mod_sns(window->v_s() - window->v_a()); |
| for (i = 0; i < num_blocks; i++) |
| bitvec_set_bit(bits, ONE); |
| |
| *bsn_begin = window->v_a(); |
| *bsn_end = window->mod_sns(*bsn_begin + num_blocks); |
| return num_blocks; |
| } |
| |
| int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc, |
| bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window) |
| { |
| int urbb_len = desc->URBB_LENGTH; |
| int crbb_len = 0; |
| int num_blocks = 0; |
| struct bitvec urbb; |
| int i; |
| bool have_bitmap; |
| int implicitly_acked_blocks; |
| int ssn = desc->STARTING_SEQUENCE_NUMBER; |
| int rc; |
| |
| if (desc->FINAL_ACK_INDICATION) |
| return handle_final_ack(bits, bsn_begin, bsn_end, window); |
| |
| if (desc->Exist_CRBB) |
| crbb_len = desc->CRBB_LENGTH; |
| |
| have_bitmap = (urbb_len + crbb_len) > 0; |
| |
| /* |
| * bow & bitmap present: |
| * V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S) |
| * bow & not bitmap present: |
| * V(A)-> [ 11111...11111 ] . SSN .... V(S) |
| * not bow & bitmap present: |
| * V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S) |
| * not bow & not bitmap present: |
| * V(A)-> ... [] . SSN .... V(S) |
| */ |
| |
| if (desc->BEGINNING_OF_WINDOW) { |
| implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a()); |
| |
| for (i = 0; i < implicitly_acked_blocks; i++) |
| bitvec_set_bit(bits, ONE); |
| |
| num_blocks += implicitly_acked_blocks; |
| } |
| |
| if (!have_bitmap) |
| goto aborted; |
| |
| /* next bit refers to V(Q) and thus is always zero (and not |
| * transmitted) */ |
| bitvec_set_bit(bits, ZERO); |
| num_blocks += 1; |
| |
| if (crbb_len > 0) { |
| int old_len = bits->cur_bit; |
| |
| LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, " |
| "CRBB LEN = %d and Starting color code = %d", |
| desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE); |
| rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH, |
| desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits); |
| if (rc < 0) { |
| LOGP(DRLCMACUL, LOGL_NOTICE, |
| "Failed to decode CRBB: length %d, data '%s'\n", |
| desc->CRBB_LENGTH, osmo_hexdump( |
| desc->CRBB, (desc->CRBB_LENGTH + 7)/8)); |
| /* We don't know the SSN offset for the URBB, |
| * return what we have so far and assume the |
| * bitmap has stopped here */ |
| goto aborted; |
| } |
| |
| LOGP(DRLCMACDL, LOGL_DEBUG, |
| "CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n", |
| desc->CRBB_LENGTH, bits->cur_bit - old_len, |
| desc->CRBB_STARTING_COLOR_CODE, |
| osmo_hexdump( |
| desc->CRBB, (desc->CRBB_LENGTH + 7)/8) |
| ); |
| |
| num_blocks += (bits->cur_bit - old_len); |
| } |
| |
| urbb.cur_bit = 0; |
| urbb.data = (uint8_t *)desc->URBB; |
| urbb.data_len = sizeof(desc->URBB); |
| |
| for (i = urbb_len; i > 0; i--) { |
| /* |
| * Set bit at the appropriate position (see 3GPP TS |
| * 44.060 12.3.1) |
| */ |
| int is_ack = bitvec_get_bit_pos(&urbb, i-1); |
| bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO); |
| } |
| num_blocks += urbb_len; |
| |
| aborted: |
| *bsn_begin = window->v_a(); |
| *bsn_end = window->mod_sns(*bsn_begin + num_blocks); |
| |
| return num_blocks; |
| } |
| |
| int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc, |
| bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window) |
| { |
| int urbb_len = RLC_GPRS_WS; |
| int num_blocks; |
| struct bitvec urbb; |
| |
| if (desc->FINAL_ACK_INDICATION) |
| return handle_final_ack(bits, bsn_begin, bsn_end, window); |
| |
| *bsn_begin = window->v_a(); |
| *bsn_end = desc->STARTING_SEQUENCE_NUMBER; |
| |
| num_blocks = window->mod_sns(*bsn_end - *bsn_begin); |
| |
| if (num_blocks < 0 || num_blocks > urbb_len) { |
| *bsn_end = *bsn_begin; |
| LOGP(DRLCMACUL, LOGL_NOTICE, |
| "Invalid GPRS Ack/Nack window %d:%d (length %d)\n", |
| *bsn_begin, *bsn_end, num_blocks); |
| return -EINVAL; |
| } |
| |
| urbb.cur_bit = 0; |
| urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP; |
| urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP); |
| |
| /* |
| * TS 44.060, 12.3: |
| * BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64. |
| * The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128. |
| * |
| * We are only interested in the range from V(A) to SSN-1 which is |
| * num_blocks large. The RBB is laid out as |
| * [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64] |
| * so we want to start with [V(A)] and go backwards until we reach |
| * [SSN-1] to get the needed BSNs in an increasing order. Note that |
| * the bit numbers are counted from the end of the buffer. |
| */ |
| for (int i = num_blocks; i > 0; i--) { |
| int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i); |
| bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO); |
| } |
| |
| return num_blocks; |
| } |