/* * 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/. * */ #include "srsenb/hdr/phy/txrx.h" #include "srslte/asn1/rrc_asn1.h" #include "srslte/common/log.h" #include "srslte/common/threads.h" #include #include #define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) #define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) #define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) #define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) using namespace std; using namespace asn1::rrc; namespace srsenb { phy_common::phy_common(uint32_t max_workers) : tx_sem(max_workers) { this->nof_workers = nof_workers; params.max_prach_offset_us = 20; radio = NULL; mac = NULL; is_first_tx = false; is_first_of_burst = false; have_mtch_stop = false; this->max_workers = max_workers; ZERO_OBJECT(ul_cfg_com); ZERO_OBJECT(dl_cfg_com); ZERO_OBJECT(ul_grants); for (uint32_t i = 0; i < max_workers; i++) { sem_init(&tx_sem[i], 0, 0); // All semaphores start blocked } } phy_common::~phy_common() { for (uint32_t i = 0; i < max_workers; i++) { sem_destroy(&tx_sem[i]); } } void phy_common::set_nof_workers(uint32_t nof_workers) { this->nof_workers = nof_workers; } void phy_common::reset() { bzero(ul_grants, sizeof(mac_interface_phy::ul_sched_t)*TTIMOD_SZ); bzero(dl_grants, sizeof(mac_interface_phy::dl_sched_t)*TTIMOD_SZ); } bool phy_common::init(srslte_cell_t* cell_, srslte::radio* radio_h_, mac_interface_phy* mac_) { radio = radio_h_; mac = mac_; memcpy(&cell, cell_, sizeof(srslte_cell_t)); pthread_mutex_init(&user_mutex, NULL); pthread_mutex_init(&mtch_mutex, NULL); pthread_cond_init(&mtch_cvar, NULL); is_first_of_burst = true; is_first_tx = true; reset(); return true; } void phy_common::stop() { for (uint32_t i=0;iset_tti(tti); radio->tx(buffer, nof_samples, tx_time); // Allow next TTI to transmit sem_post(&tx_sem[(tti+1)%nof_workers]); // Trigger MAC clock mac->tti_clock(); } void phy_common::ue_db_clear(uint32_t tti) { for(std::map::iterator iter=common_ue_db.begin(); iter!=common_ue_db.end(); ++iter) { pending_ack_t *p = &((common_ue*)&iter->second)->pending_ack; for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { p->is_pending[TTIMOD(tti)][tb_idx] = false; } } } void phy_common::ue_db_add_rnti(uint16_t rnti) { pthread_mutex_lock(&user_mutex); if (!common_ue_db.count(rnti)) { add_rnti(rnti); } pthread_mutex_unlock(&user_mutex); } // Private function not mutexed void phy_common::add_rnti(uint16_t rnti) { for (int i = 0; i < TTIMOD_SZ; i++) { for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { common_ue_db[rnti].pending_ack.is_pending[i][tb_idx] = false; } } } void phy_common::ue_db_rem_rnti(uint16_t rnti) { pthread_mutex_lock(&user_mutex); if (!common_ue_db.count(rnti)) { common_ue_db.erase(rnti); } pthread_mutex_unlock(&user_mutex); } void phy_common::ue_db_set_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t last_n_pdcch) { pthread_mutex_lock(&user_mutex); if (common_ue_db.count(rnti)) { common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = true; common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)] = (uint16_t)last_n_pdcch; } pthread_mutex_unlock(&user_mutex); } bool phy_common::ue_db_is_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t* last_n_pdcch) { bool ret = false; pthread_mutex_lock(&user_mutex); if (common_ue_db.count(rnti)) { ret = common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx]; common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = false; if (ret && last_n_pdcch) { *last_n_pdcch = common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)]; } } pthread_mutex_unlock(&user_mutex); return ret; } void phy_common::ue_db_set_ri(uint16_t rnti, uint8_t ri) { pthread_mutex_lock(&user_mutex); if (common_ue_db.count(rnti)) { common_ue_db[rnti].ri = ri; } pthread_mutex_unlock(&user_mutex); } uint8_t phy_common::ue_db_get_ri(uint16_t rnti) { pthread_mutex_lock(&user_mutex); uint8_t ret = 0; if (common_ue_db.count(rnti)) { ret = common_ue_db[rnti].ri; } pthread_mutex_unlock(&user_mutex); return ret; } void phy_common::ue_db_set_last_ul_tb(uint16_t rnti, uint32_t pid, srslte_ra_tb_t tb) { pthread_mutex_lock(&user_mutex); if (!common_ue_db.count(rnti)) { add_rnti(rnti); } common_ue_db[rnti].last_tb[pid % SRSLTE_MAX_HARQ_PROC] = tb; pthread_mutex_unlock(&user_mutex); } srslte_ra_tb_t phy_common::ue_db_get_last_ul_tb(uint16_t rnti, uint32_t pid) { pthread_mutex_lock(&user_mutex); srslte_ra_tb_t ret; if (common_ue_db.count(rnti)) { ret = common_ue_db[rnti].last_tb[pid % SRSLTE_FDD_NOF_HARQ]; } pthread_mutex_unlock(&user_mutex); return ret; } void phy_common::set_mch_period_stop(uint32_t stop) { pthread_mutex_lock(&mtch_mutex); have_mtch_stop = true; mch_period_stop = stop; pthread_cond_signal(&mtch_cvar); pthread_mutex_unlock(&mtch_mutex); } void phy_common::configure_mbsfn(phy_interface_rrc::phy_cfg_mbsfn_t* cfg) { mbsfn = *cfg; build_mch_table(); build_mcch_table(); sib13_configured = true; mcch_configured = true; } void phy_common::build_mch_table() { // First reset tables ZERO_OBJECT(mcch_table); // 40 element table represents 4 frames (40 subframes) if (mbsfn.mbsfn_subfr_cnfg.sf_alloc.type().value == mbsfn_sf_cfg_s::sf_alloc_c_::types::one_frame) { generate_mch_table(&mch_table[0], (uint32_t)mbsfn.mbsfn_subfr_cnfg.sf_alloc.one_frame().to_number(), 1); } else { generate_mch_table(&mch_table[0], (uint32_t)mbsfn.mbsfn_subfr_cnfg.sf_alloc.four_frames().to_number(), 4); } // Debug std::stringstream ss; ss << "|"; for(uint32_t j=0; j<40; j++) { ss << (int) mch_table[j] << "|"; } } void phy_common::build_mcch_table() { ZERO_OBJECT(mcch_table); generate_mcch_table(mcch_table, static_cast(mbsfn.mbsfn_area_info.mcch_cfg_r9.sf_alloc_info_r9.to_number())); std::stringstream ss; ss << "|"; for(uint32_t j=0; j<10; j++) { ss << (int) mcch_table[j] << "|"; } } bool phy_common::is_mcch_subframe(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti) { uint32_t sfn; // System Frame Number uint8_t sf; // Subframe uint8_t offset; uint8_t period; sfn = phy_tti/10; sf = phy_tti%10; if (sib13_configured) { mbsfn_area_info_r9_s* area_info = &mbsfn.mbsfn_area_info; offset = area_info->mcch_cfg_r9.mcch_offset_r9; period = area_info->mcch_cfg_r9.mcch_repeat_period_r9.to_number(); if((sfn%period == offset) && mcch_table[sf] > 0) { cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; cfg->non_mbsfn_region_length = area_info->non_mbsfn_region_len.to_number(); cfg->mbsfn_mcs = area_info->mcch_cfg_r9.sig_mcs_r9.to_number(); cfg->enable = true; cfg->is_mcch = true; have_mtch_stop = false; return true; } } return false; } bool phy_common::is_mch_subframe(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti) { uint32_t sfn; // System Frame Number uint8_t sf; // Subframe uint8_t offset; uint8_t period; sfn = phy_tti / 10; sf = phy_tti%10; // Set some defaults cfg->mbsfn_area_id = 0; cfg->non_mbsfn_region_length = 1; cfg->mbsfn_mcs = 2; cfg->enable = false; cfg->is_mcch = false; // Check for MCCH if (is_mcch_subframe(cfg, phy_tti)) { return true; } if (not mcch_configured) { return false; } // Not MCCH, check for MCH mbsfn_sf_cfg_s* subfr_cnfg = &mbsfn.mbsfn_subfr_cnfg; mbsfn_area_info_r9_s* area_info = &mbsfn.mbsfn_area_info; offset = subfr_cnfg->radioframe_alloc_offset; period = subfr_cnfg->radioframe_alloc_period.to_number(); if (subfr_cnfg->sf_alloc.type() == mbsfn_sf_cfg_s::sf_alloc_c_::types::one_frame) { if ((sfn%period == offset) && (mch_table[sf] > 0)) { if (sib13_configured) { cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; cfg->non_mbsfn_region_length = area_info->non_mbsfn_region_len.to_number(); if (mcch_configured) { // Iterate through PMCH configs to see which one applies in the current frame mbsfn_area_cfg_r9_s* area_r9 = &mbsfn.mcch.msg.c1().mbsfn_area_cfg_r9(); uint32_t frame_alloc_idx = sfn % area_r9->common_sf_alloc_period_r9.to_number(); uint32_t mbsfn_per_frame = area_r9->pmch_info_list_r9[0].pmch_cfg_r9.sf_alloc_end_r9 / +area_r9->pmch_info_list_r9[0].pmch_cfg_r9.mch_sched_period_r9.to_number(); uint32_t sf_alloc_idx = frame_alloc_idx * mbsfn_per_frame + ((sf < 4) ? sf - 1 : sf - 3); while (!have_mtch_stop) { pthread_cond_wait(&mtch_cvar, &mtch_mutex); } for (uint32_t i = 0; i < area_r9->pmch_info_list_r9.size(); i++) { if (sf_alloc_idx <= mch_period_stop) { cfg->mbsfn_mcs = mbsfn.mcch.msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[i].pmch_cfg_r9.data_mcs_r9; cfg->enable = true; } } } } return true; } } else if (subfr_cnfg->sf_alloc.type() == mbsfn_sf_cfg_s::sf_alloc_c_::types::four_frames) { uint8_t idx = sfn%period; if ((idx >= offset) && (idx < offset+4)) { if (mch_table[(idx*10)+sf] > 0){ if (sib13_configured) { cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; cfg->non_mbsfn_region_length = area_info->non_mbsfn_region_len.to_number(); // TODO: check for MCCH configuration, set MCS and decode } return true; } } } return false; } bool phy_common::is_mbsfn_sf(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti) { return is_mch_subframe(cfg, phy_tti); } }