libosmo-gprs/src/rlcmac/tbf_dl.c

308 lines
9.7 KiB
C

/* Downlink TBF as per 3GPP TS 44.064 */
/*
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program 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.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/logging.h>
#include <osmocom/gprs/rlcmac/tbf_dl.h>
#include <osmocom/gprs/rlcmac/gre.h>
#include <osmocom/gprs/rlcmac/rlc_window_dl.h>
#include <osmocom/gprs/rlcmac/rlcmac_dec.h>
#include <osmocom/gprs/rlcmac/rlcmac_enc.h>
#include <osmocom/gprs/rlcmac/pdch_ul_controller.h>
struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre)
{
struct gprs_rlcmac_dl_tbf *dl_tbf;
int rc;
dl_tbf = talloc_zero(gre, struct gprs_rlcmac_dl_tbf);
if (!dl_tbf)
return NULL;
gprs_rlcmac_tbf_constructor(dl_tbf_as_tbf(dl_tbf), GPRS_RLCMAC_TBF_DIR_DL, gre);
rc = gprs_rlcmac_tbf_dl_fsm_constructor(dl_tbf);
if (rc < 0)
goto err_tbf_destruct;
dl_tbf->tbf.nr = g_ctx->next_dl_tbf_nr++;
dl_tbf->dlw = gprs_rlcmac_rlc_dl_window_alloc(dl_tbf);
OSMO_ASSERT(dl_tbf->dlw);
dl_tbf->blkst = gprs_rlcmac_rlc_block_store_alloc(dl_tbf);
OSMO_ASSERT(dl_tbf->blkst);
return dl_tbf;
err_tbf_destruct:
gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf));
talloc_free(dl_tbf);
return NULL;
}
void gprs_rlcmac_dl_tbf_free(struct gprs_rlcmac_dl_tbf *dl_tbf)
{
if (!dl_tbf)
return;
if (dl_tbf->tbf.gre->dl_tbf == dl_tbf)
dl_tbf->tbf.gre->dl_tbf = NULL;
msgb_free(dl_tbf->llc_rx_msg);
dl_tbf->llc_rx_msg = NULL;
gprs_rlcmac_rlc_block_store_free(dl_tbf->blkst);
dl_tbf->blkst = NULL;
gprs_rlcmac_rlc_dl_window_free(dl_tbf->dlw);
dl_tbf->dlw = NULL;
gprs_rlcmac_tbf_dl_fsm_destructor(dl_tbf);
gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf));
talloc_free(dl_tbf);
}
static uint8_t dl_tbf_dl_slotmask(struct gprs_rlcmac_dl_tbf *dl_tbf)
{
uint8_t dl_slotmask = 0;
for (unsigned int i = 0; i < ARRAY_SIZE(dl_tbf->cur_alloc.ts); i++) {
if (dl_tbf->cur_alloc.ts[i].allocated)
dl_slotmask |= (1 << i);
}
return dl_slotmask;
}
int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf)
{
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
uint8_t dl_slotmask = dl_tbf_dl_slotmask(dl_tbf);
LOGPTBFDL(dl_tbf, LOGL_INFO, "Send L1CTL-CF_DL_TBF.req dl_slotmask=0x%02x dl_tfi=%u\n",
dl_slotmask, dl_tbf->cur_alloc.dl_tfi);
rlcmac_prim = gprs_rlcmac_prim_alloc_l1ctl_cfg_dl_tbf_req(dl_tbf->tbf.nr,
dl_slotmask,
dl_tbf->cur_alloc.dl_tfi);
return gprs_rlcmac_prim_call_down_cb(rlcmac_prim);
}
struct msgb *gprs_rlcmac_dl_tbf_create_pkt_dl_ack_nack(const struct gprs_rlcmac_dl_tbf *dl_tbf)
{
struct msgb *msg;
struct bitvec bv;
RlcMacUplink_t ul_block;
int rc;
OSMO_ASSERT(dl_tbf);
msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_dl_ack_nack");
if (!msg)
return NULL;
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
bv = (struct bitvec){
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
.data_len = GSM_MACBLOCK_LEN,
};
bitvec_unhex(&bv, GPRS_RLCMAC_DUMMY_VEC);
gprs_rlcmac_enc_prepare_pkt_downlink_ack_nack(&ul_block, dl_tbf);
rc = osmo_gprs_rlcmac_encode_uplink(&bv, &ul_block);
if (rc < 0) {
LOGPTBFDL(dl_tbf, LOGL_ERROR, "Encoding of Packet Downlink ACK/NACK failed (%d)\n", rc);
goto free_ret;
}
return msg;
free_ret:
msgb_free(msg);
return NULL;
}
/*
* Store received block data in LLC message(s) and forward to SGSN
* if complete.
*/
static int gprs_rlcmac_dl_tbf_assemble_forward_llc(struct gprs_rlcmac_dl_tbf *dl_tbf,
const struct gprs_rlcmac_rlc_block *blk)
{
const uint8_t *data = &blk->buf[0];
uint8_t len = blk->len;
const struct gprs_rlcmac_rlc_block_info *rdbi = &blk->block_info;
enum gprs_rlcmac_coding_scheme cs = blk->cs_last;
struct gprs_rlcmac_rlc_llc_chunk frames[16];
int i, num_frames = 0;
int rc = 0;
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Assembling frames: (len=%d)\n", len);
num_frames = gprs_rlcmac_rlc_data_from_dl_data(rdbi, cs, data,
&frames[0], ARRAY_SIZE(frames));
/* create LLC frames */
for (i = 0; i < num_frames; i++) {
struct gprs_rlcmac_rlc_llc_chunk *frame = &frames[i];
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
if (!dl_tbf->llc_rx_msg) {
rlcmac_prim = gprs_rlcmac_prim_alloc_grr_unitdata_ind(dl_tbf->tbf.gre->tlli,
NULL,
GPRS_RLCMAC_LLC_PDU_MAX_LEN);
dl_tbf->llc_rx_msg = rlcmac_prim->oph.msg;
dl_tbf->llc_rx_msg->l3h = dl_tbf->llc_rx_msg->tail;
} else {
rlcmac_prim = msgb_rlcmac_prim(dl_tbf->llc_rx_msg);
}
if (frame->length) {
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Frame %d "
"starts at offset %d, "
"length=%d, is_complete=%d\n",
i + 1, frame->offset, frame->length,
frame->is_complete);
memcpy(msgb_put(dl_tbf->llc_rx_msg, frame->length),
data + frame->offset, frame->length);
}
if (frame->is_complete) {
/* send frame to upper layers: */
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "complete UL frame len=%d\n", msgb_l3len(dl_tbf->llc_rx_msg));
rlcmac_prim->grr.ll_pdu = msgb_l3(dl_tbf->llc_rx_msg);
rlcmac_prim->grr.ll_pdu_len = msgb_l3len(dl_tbf->llc_rx_msg);
/* ownserhsip of dl_tbf->llc_rx_msg transferred here: */
rc = gprs_rlcmac_prim_call_up_cb(rlcmac_prim);
dl_tbf->llc_rx_msg = NULL;
}
}
return rc;
}
int gprs_rlcmac_dl_tbf_rcv_data_block(struct gprs_rlcmac_dl_tbf *dl_tbf,
const struct gprs_rlcmac_rlc_data_info *rlc,
uint8_t *data, uint32_t fn, uint8_t ts_nr)
{
const struct gprs_rlcmac_rlc_block_info *rdbi;
struct gprs_rlcmac_rlc_block *block;
const uint16_t ws = gprs_rlcmac_rlc_window_ws(dl_tbf->w);
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "DL DATA TFI=%d received (V(Q)=%d .. V(R)=%d)\n",
rlc->tfi,
gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw),
gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw));
unsigned int block_idx;
/* Loop over num_blocks */
for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
uint8_t *rlc_data;
rdbi = &rlc->block_info[block_idx];
LOGPTBFDL(dl_tbf, LOGL_DEBUG,
"Got %s RLC data block: FBI=%u, BSN=%d, SPB=%d, S/P=%d RRBP=%u, E=%d, bitoffs=%d\n",
gprs_rlcmac_mcs_name(rlc->cs),
rdbi->cv == 0, rdbi->bsn, rdbi->spb,
rlc->es_p, rlc->rrbp, rdbi->e,
rlc->data_offs_bits[block_idx]);
/* Check whether the block needs to be decoded */
if (!gprs_rlcmac_rlc_dl_window_is_in_window(dl_tbf->dlw, rdbi->bsn)) {
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d out of window %d..%d (it's normal)\n",
rdbi->bsn,
gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw),
gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) + ws - 1));
continue;
} else if (gprs_rlcmac_rlc_v_n_is_received(&dl_tbf->dlw->v_n, rdbi->bsn)) {
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d already received\n", rdbi->bsn);
continue;
}
/* Store block and meta info to BSN buffer */
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d storing in window (%d..%d)\n",
rdbi->bsn, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw),
gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) + ws - 1));
block = gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, rdbi->bsn);
OSMO_ASSERT(rdbi->data_len <= sizeof(block->buf));
rlc_data = &(block->buf[0]);
block->block_info = *rdbi;
block->cs_last = rlc->cs;
block->len = gprs_rlcmac_rlc_copy_to_aligned_buffer(rlc, block_idx,
data, rlc_data);
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "data_length=%d, data=%s\n",
block->len, osmo_hexdump(rlc_data, block->len));
gprs_rlcmac_rlc_dl_window_receive_bsn(dl_tbf->dlw, 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 = gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw);
const uint16_t count = gprs_rlcmac_rlc_dl_window_raise_v_q(dl_tbf->dlw);
/* Retrieve LLC frames from blocks that are ready */
for (uint16_t i = 0; i < count; ++i) {
uint16_t index = gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, v_q_beg + i);
gprs_rlcmac_dl_tbf_assemble_forward_llc(dl_tbf, gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, index));
}
/* Last frame in buffer: */
uint16_t last = gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w,
gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw) - 1);
block = gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, last);
rdbi = &block->block_info;
/* Check if we already received all data TBF had to send: */
if (//this->state_is(TBF_ST_FLOW) && /* still in flow state */
(gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) == gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw)) && /* if complete */
block->len) { /* if there was ever a last block received */
LOGPTBFDL(dl_tbf, LOGL_DEBUG,
"No gaps in received block, last block: BSN=%d FBI=%d\n",
rdbi->bsn, rdbi->cv == 0);
if (rdbi->cv == 0) {
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Finished with DL TBF\n");
osmo_fsm_inst_dispatch(dl_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD, NULL);
}
}
/* If RRBP contains valid data, schedule a DL ACK/NACK. */
if (rlc->es_p) {
uint32_t poll_fn = rrbp2fn(fn, rlc->rrbp);
gprs_rlcmac_pdch_ulc_reserve(g_ctx->sched.ulc[ts_nr], poll_fn,
GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK,
dl_tbf_as_tbf(dl_tbf));
}
return 0;
}