/** * * \section COPYRIGHT * * Copyright 2013-2015 The srsLTE Developers. See the * COPYRIGHT file at the top-level directory of this distribution. * * \section LICENSE * * This file is part of the srsLTE library. * * 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 "srsapps/ue/mac/mux.h" #include "srsapps/ue/mac/mac.h" namespace srslte { namespace ue { mux::mux() : pdu_msg(MAX_NOF_SUBHEADERS) { msg3_buff.init(1, MSG3_BUFF_SZ); pthread_mutex_init(&mutex, NULL); msg3_has_been_transmitted = false; for (int i=0;iget_buffer_state(i)) { return true; } } return false; } bool mux::is_pending_sdu(uint32_t lch_id) { return rlc->get_buffer_state(lch_id)>0; } void mux::set_priority(uint32_t lch_id, uint32_t set_priority, int set_PBR, uint32_t set_BSD) { pthread_mutex_lock(&mutex); if (lch_id < NOF_UL_LCH) { priority[lch_id] = set_priority; PBR[lch_id] = set_PBR; BSD[lch_id] = set_BSD; // Insert priority in sorted idx array int new_index = 0; while(set_priority > priority_sorted[new_index] && new_index < NOF_UL_LCH) { new_index++; } int old_index = 0; while(lch_id != lchid_sorted[old_index] && new_index < NOF_UL_LCH) { old_index++; } if (new_index == NOF_UL_LCH) { Error("Can't find LchID=%d in sorted list\n", lch_id); return; } // Replace goes in one direction or the other int add=new_index>old_index?1:-1; for (int i=old_index;i!=new_index;i+=add) { priority_sorted[i] = priority_sorted[i+add]; lchid_sorted[i] = lchid_sorted[i+add]; } priority_sorted[new_index] = set_priority; lchid_sorted[new_index] = lch_id; } pthread_mutex_unlock(&mutex); } sch_subh::cetype bsr_format_convert(bsr_proc::bsr_format_t format) { switch(format) { case bsr_proc::LONG_BSR: return sch_subh::LONG_BSR; case bsr_proc::SHORT_BSR: return sch_subh::SHORT_BSR; case bsr_proc::TRUNC_BSR: return sch_subh::TRUNC_BSR; } } // Multiplexing and logical channel priorization as defined in Section 5.4.3 uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz) { pthread_mutex_lock(&mutex); // Update Bj for (int i=0;i= 0) { Bj[i] += PBR[i]; } if (Bj[i] >= BSD[i]) { Bj[i] = BSD[i]; } } // Logical Channel Procedure pdu_msg.init_tx(payload, pdu_sz, true); // MAC control element for C-RNTI or data from UL-CCCH bool is_first = true; if (!allocate_sdu(0, &pdu_msg, &is_first)) { if (pending_crnti_ce) { if (pdu_msg.new_subh()) { pdu_msg.next(); if (!pdu_msg.get()->set_c_rnti(pending_crnti_ce)) { Warning("Pending C-RNTI CE could not be inserted in MAC PDU\n"); } } } } pending_crnti_ce = 0; uint32_t bsr_payload_sz = bsr_procedure->need_to_send_bsr_on_ul_grant(pdu_msg.rem_size()); bsr_proc::bsr_t bsr; // MAC control element for BSR, with exception of BSR included for padding; sch_subh *bsr_subh = NULL; if (bsr_payload_sz) { Debug("Including BSR CE size %d\n", bsr_payload_sz); if (pdu_msg.new_subh()) { pdu_msg.next(); bsr_subh = pdu_msg.get(); pdu_msg.update_space_ce(bsr_payload_sz); } } // MAC control element for PHR if (!phr_included) { if (pdu_msg.new_subh()) { phr_included = true; pdu_msg.next(); pdu_msg.get()->set_phr(46); } } // data from any Logical Channel, except data from UL-CCCH; // first only those with positive Bj uint32_t sdu_sz = 0; for (int i=1;i 0 || PBR[lcid] < 0) && res) { res = allocate_sdu(lcid, &pdu_msg, (PBR[lcid]<0)?-1:Bj[lcid], &sdu_sz, &is_first); if (res && PBR[lcid] >= 0) { Bj[lcid] -= sdu_sz; } } } } // If resources remain, allocate regardless of their Bj value for (int i=1;igenerate_bsr_on_ul_grant(pdu_msg.rem_size(), &bsr); // Insert Padding BSR if not inserted Regular/Periodic BSR if (!bsr_payload_sz && send_bsr) { if (pdu_msg.new_subh()) { pdu_msg.next(); bsr_subh = pdu_msg.get(); } } // And set the BSR if (bsr_subh) { bsr_subh->set_bsr(bsr.buff_size, bsr_format_convert(bsr.format), bsr_payload_sz?false:true); } Debug("Assembled MAC PDU msg size %d/%d bytes\n", pdu_msg.size(), pdu_sz); //pdu_msg.fprint(stdout); pthread_mutex_unlock(&mutex); /* Generate MAC PDU and save to buffer */ return pdu_msg.write_packet(); } void mux::append_crnti_ce_next_tx(uint16_t crnti) { pending_crnti_ce = crnti; } bool mux::allocate_sdu(uint32_t lcid, sch_pdu *pdu_msg) { return allocate_sdu(lcid, pdu_msg, -1, NULL, NULL); } bool mux::allocate_sdu(uint32_t lcid, sch_pdu *pdu_msg, bool *is_first) { return allocate_sdu(lcid, pdu_msg, -1, NULL, is_first); } bool mux::allocate_sdu(uint32_t lcid, sch_pdu *pdu_msg, int max_sdu_sz, uint32_t *sdu_sz, bool *is_first) { // Get n-th pending SDU pointer and length int sdu_len = rlc->get_buffer_state(lcid); if (sdu_len > 0) { // there is pending SDU to allocate int buffer_state = sdu_len; if (sdu_len > max_sdu_sz && max_sdu_sz >= 0) { sdu_len = max_sdu_sz; } if (sdu_len > pdu_msg->rem_size() - 2) { sdu_len = pdu_msg->rem_size() - 2; } if (sdu_len > MIN_RLC_SDU_LEN) { if (pdu_msg->new_subh()) { // there is space for a new subheader pdu_msg->next(); sdu_len = pdu_msg->get()->set_sdu(lcid, sdu_len, rlc, is_first?*is_first:false); if (sdu_len >= 0) { // new SDU could be added if (is_first) { *is_first = false; } if (sdu_sz) { *sdu_sz = sdu_len; } Info("Allocated SDU lcid=%d nbytes=%d, buffer_state=%d\n", lcid, sdu_len, buffer_state); return true; } else { Info("Could not add SDU rem_size=%d, sdu_len=%d\n", pdu_msg->rem_size(), buffer_state); pdu_msg->del_subh(); } } } } return false; } void mux::msg3_flush() { Info("Msg3 buffer flushed\n"); msg3_buff.flush(); msg3_has_been_transmitted = false; } bool mux::msg3_is_transmitted() { return msg3_has_been_transmitted; } bool mux::pdu_move_to_msg3(uint32_t pdu_sz) { uint8_t *msg3_start = (uint8_t*) msg3_buff.request(); if (msg3_start) { uint8_t *msg3_pdu = pdu_get(msg3_start, pdu_sz); if (msg3_pdu) { memmove(msg3_start, msg3_pdu, pdu_sz*sizeof(uint8_t)); msg3_buff.push(pdu_sz); return true; } else { Error("Assembling PDU\n"); } } else { Error("Generating PDU: PDU pending in buffer for transmission\n"); } return false; } /* Returns a pointer to the Msg3 buffer */ uint8_t* mux::msg3_get(uint8_t *payload, uint32_t pdu_sz) { if (msg3_buff.isempty()) { Info("Moving PDU from Mux unit to Msg3 buffer\n"); if (!pdu_move_to_msg3(pdu_sz)) { Error("Moving PDU from Mux unit to Msg3 buffer\n"); return NULL; } } uint8_t *msg3 = (uint8_t*) msg3_buff.pop(); if (msg3) { memcpy(payload, msg3, sizeof(uint8_t)*pdu_sz); msg3_has_been_transmitted = true; return payload; } else { Error("Generating Msg3\n"); } return NULL; } } }