/* Copied from tbf.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 #include #include extern "C" { #include #include } #include #include /* After receiving these frames, we send ack/nack. */ #define SEND_ACK_AFTER_FRAMES 20 extern void *tall_pcu_ctx; /* * Store received block data in LLC message(s) and forward to SGSN * if complete. */ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data) { const uint8_t *data = _data->block; uint8_t len = _data->len; const struct gprs_rlc_data_block_info *rdbi = &_data->block_info; GprsCodingScheme cs = _data->cs; Decoding::RlcData frames[16], *frame; int i, num_frames = 0; uint32_t dummy_tlli; LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len); num_frames = Decoding::rlc_data_from_ul_data( rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames), &dummy_tlli); /* create LLC frames */ for (i = 0; i < num_frames; i++) { frame = frames + i; bts->rlc_ul_payload_bytes(frame->length); LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset %d, " "length=%d, is_complete=%d\n", i + 1, frame->offset, frame->length, frame->is_complete); m_llc.append_frame(data + frame->offset, frame->length); m_llc.consume(frame->length); if (frame->is_complete) { /* 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(); bts->llc_ul_bytes(m_llc.frame_length()); m_llc.reset(); } } return 0; } struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts) { int final = (state_is(GPRS_RLCMAC_FINISHED)); struct msgb *msg; int rc; unsigned int rrbp = 0; uint32_t new_poll_fn = 0; if (final) { if (poll_state == GPRS_RLCMAC_POLL_SCHED && ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " "scheduled for %s, so we must wait for " "the final uplink ack...\n", tbf_name(this)); return NULL; } rc = check_polling(fn, ts, &new_poll_fn, &rrbp); if (rc < 0) 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"); Encoding::write_packet_uplink_ack(bts_data(), ack_vec, this, final, rrbp); bitvec_pack(ack_vec, msgb_put(msg, 23)); bitvec_free(ack_vec); /* now we must set this flag, so we are allowed to assign downlink * TBF on PACCH. it is only allowed when TLLI is acknowledged. */ m_contention_resolution_done = 1; if (final) { set_polling(new_poll_fn, ts); /* waiting for final acknowledge */ ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; m_final_ack_sent = 1; } else ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; return msg; } /*! \brief receive data from PDCH/L1 */ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged( const struct gprs_rlc_data_info *rlc, uint8_t *data, struct pcu_l1_meas *meas) { int8_t rssi = meas->have_rssi ? meas->rssi : 0; const uint16_t ws = m_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", rlc->tfi, this->m_window.v_q(), this->m_window.v_r()); /* process RSSI */ gprs_rlcmac_rssi(this, rssi); /* store measurement values */ if (ms()) ms()->update_l1_meas(meas); uint32_t new_tlli = 0; unsigned int block_idx; /* restart T3169 */ tbf_timer_start(this, 3169, bts_data()->t3169, 0); /* Increment RX-counter */ this->m_rx_counter++; /* Loop over num_blocks */ for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) { int num_chunks; uint8_t *rlc_data; const struct gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx]; bool need_rlc_data = false; struct gprs_rlc_data *block; LOGP(DRLCMACUL, LOGL_DEBUG, "%s: Got %s RLC data block: " "CV=%d, BSN=%d, SPB=%d, " "PI=%d, E=%d, TI=%d, bitoffs=%d\n", name(), rlc->cs.name(), rdbi->cv, rdbi->bsn, rdbi->spb, rdbi->pi, rdbi->e, rdbi->ti, rlc->data_offs_bits[block_idx]); /* Check whether the block needs to be decoded */ if (!m_window.is_in_window(rdbi->bsn)) { LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " "%d..%d (it's normal)\n", rdbi->bsn, m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1)); } else if (m_window.is_received(rdbi->bsn)) { LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d already received\n", rdbi->bsn); } else { need_rlc_data = true; } if (!is_tlli_valid()) { if (!rdbi->ti) { LOGP(DRLCMACUL, LOGL_NOTICE, "%s: Missing TLLI within UL DATA.\n", name()); continue; } need_rlc_data = true; } if (!need_rlc_data) continue; /* Store block and meta info to BSN buffer */ LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", rdbi->bsn, m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1)); block = m_rlc.block(rdbi->bsn); block->block_info = *rdbi; block->cs = rlc->cs; OSMO_ASSERT(rdbi->data_len <= sizeof(block->block)); rlc_data = &(block->block[0]); /* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if * 2nd part. Note that resegmentation is currently disabled * within the UL assignment. */ if (rdbi->spb) { LOGP(DRLCMACUL, LOGL_NOTICE, "Got SPB != 0 but resegmentation has been " "disabled, skipping %s data block with BSN %d, " "TFI=%d.\n", rlc->cs.name(), rdbi->bsn, rlc->tfi); continue; } block->len = Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data, rlc_data); LOGP(DRLCMACUL, LOGL_DEBUG, "%s: data_length=%d, data=%s\n", name(), block->len, osmo_hexdump(rlc_data, block->len)); /* TODO: Handle SPB != 0 -> set state to partly received * (upper/lower) and continue with the loop, unless the other * part is already present. */ /* Get/Handle TLLI */ if (rdbi->ti) { num_chunks = Decoding::rlc_data_from_ul_data( rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli); if (num_chunks < 0) { bts->decode_error(); LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI of %s UL DATA " "TFI=%d.\n", rlc->cs.name(), rlc->tfi); m_window.invalidate_bsn(rdbi->bsn); continue; } if (!this->is_tlli_valid()) { if (!new_tlli) { LOGP(DRLCMACUL, LOGL_NOTICE, "%s: TLLI = 0 within UL DATA.\n", name()); m_window.invalidate_bsn(rdbi->bsn); continue; } LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of " "UL DATA TFI=%d.\n", tlli(), rlc->tfi); set_tlli_from_ul(new_tlli); } else if (new_tlli && new_tlli != tlli()) { LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " "DATA TFI=%d. (Ignoring due to contention " "resolution)\n", rlc->tfi); m_window.invalidate_bsn(rdbi->bsn); continue; } } m_window.receive_bsn(rdbi->bsn); } /* 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 = m_window.v_q(); const uint16_t count = m_window.raise_v_q(); /* Retrieve LLC frames from blocks that are ready */ for (uint16_t i = 0; i < count; ++i) { uint16_t index = m_window.mod_sns(v_q_beg + i); 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->m_window.v_q() == this->m_window.v_r()) { /* if complete */ struct gprs_rlc_data *block = m_rlc.block(m_window.mod_sns(m_window.v_r() - 1)); const struct gprs_rlc_data_block_info *rdbi = &block->block_info; LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " "last block: BSN=%d CV=%d\n", rdbi->bsn, rdbi->cv); if (rdbi->cv == 0) { LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " "TBF\n"); set_state(GPRS_RLCMAC_FINISHED); /* Reset N3103 counter. */ this->m_n3103 = 0; } } /* If TLLI is included or if we received half of the window, we send * an ack/nack */ maybe_schedule_uplink_acknack(rlc); return 0; } void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack( const gprs_rlc_data_info *rlc) { bool have_ti = rlc->block_info[0].ti || (rlc->num_data_blocks > 1 && rlc->block_info[1].ti); if (rlc->si || have_ti || state_is(GPRS_RLCMAC_FINISHED) || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { if (rlc->si) { LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " "because MS is stalled.\n"); } if (have_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 ((m_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_ul_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; }