/* Downlink TBF as per 3GPP TS 44.064 */ /* * (C) 2023 by sysmocom - s.f.m.c. GmbH * * 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 . * */ #include #include #include #include #include #include #include static void gprs_rlcmac_dl_tbf_t3190_timer_cb(void *data); static void gprs_rlcmac_dl_tbf_t3192_timer_cb(void *data); 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_rlcmac_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); osmo_timer_setup(&dl_tbf->t3190, gprs_rlcmac_dl_tbf_t3190_timer_cb, dl_tbf); osmo_timer_setup(&dl_tbf->t3192, gprs_rlcmac_dl_tbf_t3192_timer_cb, dl_tbf); 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) { struct gprs_rlcmac_tbf *tbf; struct gprs_rlcmac_entity *gre; if (!dl_tbf) return; tbf = dl_tbf_as_tbf(dl_tbf); gre = tbf->gre; osmo_timer_del(&dl_tbf->t3190); osmo_timer_del(&dl_tbf->t3192); 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(tbf); talloc_free(dl_tbf); /* Inform the MS that the TBF pointer has been freed: */ gprs_rlcmac_entity_dl_tbf_freed(gre, dl_tbf); } static void gprs_rlcmac_dl_tbf_t3190_timer_cb(void *data) { struct gprs_rlcmac_dl_tbf *dl_tbf = data; LOGPTBFDL(dl_tbf, LOGL_NOTICE, "Timeout of T3190\n"); gprs_rlcmac_dl_tbf_free(dl_tbf); } void gprs_rlcmac_dl_tbf_t3190_start(struct gprs_rlcmac_dl_tbf *dl_tbf) { unsigned long val_sec; val_sec = osmo_tdef_get(g_rlcmac_ctx->T_defs, 3190, OSMO_TDEF_S, -1); osmo_timer_schedule(&dl_tbf->t3190, val_sec, 0); } /* This timer is used on the mobile station side when the mobile station has * received all of the RLC data blocks. When timer T3192 expires the mobile station * shall release the resources associated with the TBF (e.g. TFI) and begin to * monitor its paging channel. */ static void gprs_rlcmac_dl_tbf_t3192_timer_cb(void *data) { struct gprs_rlcmac_dl_tbf *dl_tbf = data; LOGPTBFDL(dl_tbf, LOGL_INFO, "Timeout of T3192\n"); gprs_rlcmac_dl_tbf_free(dl_tbf); } void gprs_rlcmac_dl_tbf_t3192_start(struct gprs_rlcmac_dl_tbf *dl_tbf) { unsigned long val_msec; val_msec = osmo_tdef_get(g_rlcmac_ctx->T_defs, 3192, OSMO_TDEF_MS, -1); LOGPTBFDL(dl_tbf, LOGL_INFO, "Starting T3192 (%lu ms)\n", val_msec); OSMO_ASSERT(gprs_rlcmac_tbf_dl_state(dl_tbf) == GPRS_RLCMAC_TBF_DL_ST_FINISHED); osmo_timer_schedule(&dl_tbf->t3192, val_msec / 1000, (val_msec % 1000) * 1000); } 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-CFG_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(struct gprs_rlcmac_dl_tbf *dl_tbf, const struct gprs_rlcmac_rts_block_ind *bi) { struct msgb *msg; struct bitvec bv; RlcMacUplink_t ul_block; bool chan_req = false; struct gprs_rlcmac_entity *gre = dl_tbf->tbf.gre; int rc; OSMO_ASSERT(dl_tbf); msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_dl_ack_nack"); if (!msg) return NULL; /* Channel Request Description. Request a UL-TBF if we have UL data * queued to send and no active UL TBF (TS 44.060 8.1.2.5) */ if (!gre->ul_tbf && gprs_rlcmac_entity_have_tx_data_queued(gre)) { gre->ul_tbf = gprs_rlcmac_ul_tbf_alloc(gre); rc = gprs_rlcmac_tbf_ul_ass_start_from_dl_tbf_ack_nack(gre->ul_tbf, dl_tbf, bi->ts); if (rc < 0) { LOGPTBFDL(dl_tbf, LOGL_ERROR, "FN=%u TS=%u Failed starting assignment of requested UL TBF (%d)\n", bi->fn, bi->ts, rc); } else { LOGPTBFDL(dl_tbf, LOGL_DEBUG, "FN=%u TS=%u Requesting UL TBF through PKT DL ACK/NACK\n", bi->fn, bi->ts); chan_req = true; } } /* 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, chan_req); 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; } /* Transmitting final Downlink Ack/Nack: Stop T3190 (if still armed), re-arm T3192 */ if (gprs_rlcmac_tbf_dl_state(dl_tbf) == GPRS_RLCMAC_TBF_DL_ST_FINISHED) { osmo_timer_del(&dl_tbf->t3190); gprs_rlcmac_dl_tbf_t3192_start(dl_tbf); } 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; unsigned int block_idx; 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)); /* Re-arm T3190: */ gprs_rlcmac_dl_tbf_t3190_start(dl_tbf); /* 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_rlcmac_ctx->sched.ulc[ts_nr], poll_fn, GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK, dl_tbf_as_tbf(dl_tbf)); } return 0; }