/* * Copyright 2013-2019 Software Radio Systems Limited * * This file is part of srsLTE. * * srsLTE is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * srsLTE 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 Affero General Public License for more details. * * A copy of the GNU Affero General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ #define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) #define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) #define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) #define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) #include "srsue/hdr/stack/mac/ul_harq.h" #include "srslte/common/interfaces_common.h" #include "srslte/common/log.h" #include "srslte/common/mac_pcap.h" #include "srslte/common/timers.h" #include "srslte/interfaces/ue_interfaces.h" namespace srsue { ul_harq_entity::ul_harq_entity() : proc(SRSLTE_MAX_HARQ_PROC) { pcap = NULL; mux_unit = NULL; ra_procedure = NULL; log_h = NULL; rntis = NULL; average_retx = 0; nof_pkts = 0; } bool ul_harq_entity::init(srslte::log* log_h_, mac_interface_rrc_common::ue_rnti_t* rntis_, ra_proc* ra_procedure_, mux* mux_unit_) { log_h = log_h_; mux_unit = mux_unit_; ra_procedure = ra_procedure_; rntis = rntis_; for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) { if (!proc[i].init(i, this)) { return false; } } return true; } void ul_harq_entity::reset() { for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) { proc[i].reset(); } ul_sps_assig.clear(); } void ul_harq_entity::reset_ndi() { for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) { proc[i].reset_ndi(); } } void ul_harq_entity::set_config(srsue::mac_interface_rrc_common::ul_harq_cfg_t& harq_cfg) { this->harq_cfg = harq_cfg; } void ul_harq_entity::start_pcap(srslte::mac_pcap* pcap_) { pcap = pcap_; } /***************** PHY->MAC interface for UL processes **************************/ void ul_harq_entity::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action) { bzero(action, sizeof(mac_interface_phy_lte::tb_action_ul_t)); if (grant.pid >= SRSLTE_MAX_HARQ_PROC) { Error("Invalid PID: %d\n", grant.pid); return; } if (grant.rnti == rntis->crnti || grant.rnti == rntis->temp_rnti || SRSLTE_RNTI_ISRAR(grant.rnti)) { if (grant.rnti == rntis->crnti && proc[grant.pid].is_sps()) { grant.tb.ndi = true; } proc[grant.pid].new_grant_ul(grant, action); } else if (grant.rnti == rntis->sps_rnti) { if (grant.tb.ndi) { grant.tb.ndi = proc[grant.pid].get_ndi(); proc[grant.pid].new_grant_ul(grant, action); } else { Info("Not implemented\n"); } } else { Warning("Received grant for unknnown rnti=0x%x\n", grant.rnti); } } int ul_harq_entity::get_current_tbs(uint32_t pid) { if (pid >= SRSLTE_MAX_HARQ_PROC) { Error("Invalid PID: %d\n", pid); return 0; } return proc[pid].get_current_tbs(); } float ul_harq_entity::get_average_retx() { return average_retx; } ul_harq_entity::ul_harq_process::ul_harq_process() { log_h = NULL; pdu_ptr = NULL; payload_buffer = NULL; bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); harq_feedback = false; is_initiated = false; is_grant_configured = false; pid = 0; current_tx_nb = 0; current_irv = 0; } ul_harq_entity::ul_harq_process::~ul_harq_process() { if (is_initiated) { if (payload_buffer) { free(payload_buffer); } srslte_softbuffer_tx_free(&softbuffer); } } bool ul_harq_entity::ul_harq_process::init(uint32_t pid, ul_harq_entity* parent) { if (srslte_softbuffer_tx_init(&softbuffer, 110)) { ERROR("Error initiating soft buffer\n"); return false; } harq_entity = parent; log_h = harq_entity->log_h; is_initiated = true; this->pid = pid; payload_buffer = (uint8_t*)srslte_vec_malloc(payload_buffer_len * sizeof(uint8_t)); if (!payload_buffer) { Error("Allocating memory\n"); return false; } pdu_ptr = payload_buffer; return true; } void ul_harq_entity::ul_harq_process::reset() { current_tx_nb = 0; current_irv = 0; is_grant_configured = false; bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); } void ul_harq_entity::ul_harq_process::reset_ndi() { cur_grant.tb.ndi = false; } #define grant_is_rar() (grant.rnti == harq_entity->rntis->temp_rnti) void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action) { if (grant.phich_available) { if (grant.tb.ndi_present && (grant.tb.ndi == get_ndi()) && (grant.tb.tbs != 0)) { harq_feedback = false; } else { harq_feedback = grant.hi_value; } if (grant.rnti == harq_entity->rntis->crnti && harq_entity->ra_procedure->is_contention_resolution()) { harq_entity->ra_procedure->pdcch_to_crnti(true); } // Get maximum retransmissions uint32_t max_retx; if (grant_is_rar()) { max_retx = harq_entity->harq_cfg.max_harq_msg3_tx; } else { max_retx = harq_entity->harq_cfg.max_harq_tx; } // Check maximum retransmissions, do not consider last retx ACK if (current_tx_nb >= max_retx && !grant.hi_value) { Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx); if (grant_is_rar()) { harq_entity->ra_procedure->harq_max_retx(); } reset(); } else if (grant_is_rar()) { harq_entity->ra_procedure->harq_retx(); } } // Reset HARQ process if TB has changed if (harq_feedback && has_grant() && grant.tb.ndi_present) { if (grant.tb.tbs != cur_grant.tb.tbs && cur_grant.tb.tbs > 0 && grant.tb.tbs > 0) { Debug( "UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d\n", pid, cur_grant.tb.tbs, grant.tb.tbs); reset(); } } // Receive and route HARQ feedbacks if (grant.tb.ndi_present) { /* If TBS != 0, it's a CQI request, don't read PDU from RLC */ if (grant.tb.tbs == 0) { action->tb.enabled = true; } else if ((grant.rnti == harq_entity->rntis->crnti && // If C-RNTI ((grant.tb.ndi != get_ndi()) || !has_grant())) || // if NDI toggled or is first dci is a new tx grant_is_rar()) // If T-CRNTI received in RAR has no RV information { // New transmission reset(); // Check buffer size if (grant.tb.tbs > payload_buffer_len) { Error("Grant size exceeds payload buffer size (%d > %d)\n", grant.tb.tbs, payload_buffer_len); return; } // Uplink dci in a RAR and there is a PDU in the Msg3 buffer if (grant_is_rar()) { if (harq_entity->mux_unit->msg3_is_pending()) { Debug("Getting Msg3 buffer payload, dci size=%d bytes\n", grant.tb.tbs); pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer, grant.tb.tbs); if (pdu_ptr) { generate_new_tx(grant, action); } else { Warning("UL RAR dci available but no Msg3 on buffer\n"); } } else { Warning("UL RAR available but no Msg3 pending on buffer\n"); } // Normal UL dci } else { // Request a MAC PDU from the Multiplexing & Assemble Unit pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer, grant.tb.tbs, pid); if (pdu_ptr) { generate_new_tx(grant, action); } else { Warning("Uplink dci but no MAC PDU in Multiplex Unit buffer\n"); } } } else if (has_grant()) { // Adaptive Re-TX generate_retx(grant, action); } else { Warning("UL %d: Received retransmission but no previous dci available for this PID.\n", pid); } if (harq_entity->pcap) { uint16_t rnti; if (grant_is_rar() && harq_entity->rntis->temp_rnti) { rnti = harq_entity->rntis->temp_rnti; } else { rnti = harq_entity->rntis->crnti; } harq_entity->pcap->write_ul_crnti(pdu_ptr, grant.tb.tbs, rnti, get_nof_retx(), 0); } } else if (has_grant()) { // Non-Adaptive Re-Tx generate_retx(grant, action); } } uint32_t ul_harq_entity::ul_harq_process::get_rv() { int rv_of_irv[4] = {0, 2, 3, 1}; return rv_of_irv[current_irv % 4]; } bool ul_harq_entity::ul_harq_process::has_grant() { return is_grant_configured; } bool ul_harq_entity::ul_harq_process::get_ndi() { return cur_grant.tb.ndi; } bool ul_harq_entity::ul_harq_process::is_sps() { return false; } uint32_t ul_harq_entity::ul_harq_process::get_nof_retx() { return current_tx_nb; } int ul_harq_entity::ul_harq_process::get_current_tbs() { return cur_grant.tb.tbs; } // Retransmission with or w/o dci (Section 5.4.2.2) void ul_harq_entity::ul_harq_process::generate_retx(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action) { int irv_of_rv[4] = {0, 3, 1, 2}; // HARQ entity requests an adaptive transmission if (grant.tb.ndi_present) { if (grant.tb.rv) { current_irv = irv_of_rv[grant.tb.rv % 4]; } Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d, HI=%s, ndi=%d, prev_ndi=%d\n", pid, current_tx_nb, get_rv(), grant.tb.tbs, harq_feedback ? "ACK" : "NACK", grant.tb.ndi, cur_grant.tb.ndi); cur_grant = grant; harq_feedback = false; generate_tx(action); // HARQ entity requests a non-adaptive transmission } else if (!harq_feedback) { // Non-adaptive retx are only sent if HI=NACK. If HI=ACK but no dci was received do not reset PID Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d, HI=%s\n", pid, current_tx_nb, get_rv(), cur_grant.tb.tbs, harq_feedback ? "ACK" : "NACK"); generate_tx(action); } } // New transmission (Section 5.4.2.2) void ul_harq_entity::ul_harq_process::generate_new_tx(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action) { // Compute average number of retransmissions per packet considering previous packet harq_entity->average_retx = SRSLTE_VEC_CMA((float)current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++); cur_grant = grant; harq_feedback = false; is_grant_configured = true; current_tx_nb = 0; current_irv = 0; Info("UL %d: New TX%s, RV=%d, TBS=%d\n", pid, grant_is_rar() ? " for Msg3" : "", get_rv(), cur_grant.tb.tbs); generate_tx(action); } // Transmission of pending frame (Section 5.4.2.2) void ul_harq_entity::ul_harq_process::generate_tx(mac_interface_phy_lte::tb_action_ul_t* action) { current_tx_nb++; action->current_tx_nb = current_tx_nb; action->expect_ack = true; action->tb.rv = cur_grant.tb.rv > 0 ? cur_grant.tb.rv : get_rv(); action->tb.enabled = true; action->tb.payload = pdu_ptr; action->tb.softbuffer.tx = &softbuffer; current_irv = (current_irv + 1) % 4; } } // namespace srsue