diff options
Diffstat (limited to 'src/common/asci.c')
-rw-r--r-- | src/common/asci.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/src/common/asci.c b/src/common/asci.c new file mode 100644 index 00000000..ccaacd8e --- /dev/null +++ b/src/common/asci.c @@ -0,0 +1,190 @@ +/* ASCI (VGCS/VBS) related common code */ + +/* (C) 2023 by Harald Welte <laforge@osmocom.org> + * + * 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 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 <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/rsl.h> + +#include <osmo-bts/bts.h> +#include <osmo-bts/bts_model.h> +#include <osmo-bts/rsl.h> +#include <osmo-bts/logging.h> +#include <osmo-bts/l1sap.h> +#include <osmo-bts/asci.h> + +static int tx_vgcs_ul_grant(struct gsm_lchan *lchan) +{ + struct gsm0408_vgcs_ul_grant ul_grant; + struct gsm_time gt; + struct msgb *msg; + + gsm_fn2gsmtime(>, lchan->asci.fn); + + /* build the RR VGCS UPLINK GRANT message as per TS 44.018 Section 9.1.49 */ + ul_grant = (struct gsm0408_vgcs_ul_grant) { + .hdr = { + .proto_discr = GSM48_PDISC_RR, + .msg_type = GSM48_MT_RR_VGCS_UPL_GRANT, + }, + .req_ref = { + .ra = lchan->asci.ref, + .t1 = gt.t1, + .t2 = gt.t2, + .t3_low = gt.t3 & 7, + .t3_high = gt.t3 >> 3, + }, + .ta = lchan->ta_ctrl.current, + }; + + /* Wrap it in a RSL UNITDATA REQUEST */ + msg = rsl_rll_simple(RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan), 0x00, 0); + msg->l3h = msg->tail; /* emulate rsl_rx_rll() behaviour */ + msgb_tl16v_put(msg, RSL_IE_L3_INFO, sizeof(ul_grant), (uint8_t *) &ul_grant); + + /* send it towards MS, just like a RSL message from the BSC */ + return lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch); +} + +/* timer call-back for T3115 (VGCS UPLINK GRANT re-transmit) */ +static void vgcs_t3115_cb(void *data) +{ + struct gsm_lchan *lchan = data; + struct gsm_bts *bts = lchan->ts->trx->bts; + + LOGPLCHAN(lchan, DASCI, LOGL_INFO, "T3115 timeout (%d resends left)\n", + bts->ny2 - lchan->asci.vgcs_ul_grant_count); + + if (lchan->state != LCHAN_S_ACTIVE) { + LOGPLCHAN(lchan, DASCI, LOGL_NOTICE, "is not active. It is in state %s. Ignoring\n", + gsm_lchans_name(lchan->state)); + return; + } + + if (lchan->asci.vgcs_ul_grant_count >= bts->ny2) { + lchan->asci.vgcs_ul_grant_count = 0; + LOGPLCHAN(lchan, DASCI, LOGL_NOTICE, "NY2 reached, sending CONNection FAILure to BSC.\n"); + rsl_tx_conn_fail(lchan, RSL_ERR_TALKER_ACC_FAIL); + lchan->asci.talker_active = VGCS_TALKER_NONE; + return; + } + + tx_vgcs_ul_grant(lchan); + lchan->asci.vgcs_ul_grant_count++; + osmo_timer_schedule(&lchan->asci.t3115, 0, bts->t3115_ms * 1000); +} + +/* Received random access on dedicated channel. */ +void vgcs_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay, uint32_t fn) +{ + LOGPLCHAN(lchan, DASCI, LOGL_NOTICE, "VGCS RACH on dedicated channel type %s received with " + "TA=%u, ref=%u\n", gsm_lchant_name(lchan->type), acc_delay, ra); + + if (ra == 0x25) { /* See TS 44.018 Table 9.1.45.1 */ + /* Listener Detection (TS 48.058 Section 4.14) */ + if (!lchan->asci.listener_detected) { + rsl_tx_listener_det(lchan, &acc_delay); + lchan->asci.listener_detected = true; + } + } else { + /* Talker Detection (TS 48.058 Section 4.13) */ + struct gsm_bts *bts = lchan->ts->trx->bts; + + /* Talker detection on group channels only */ + if (!rsl_chan_rt_is_vgcs(lchan->rsl_chan_rt)) + return; + + if (lchan->asci.talker_active != VGCS_TALKER_NONE) { + LOGPLCHAN(lchan, DASCI, LOGL_DEBUG, "Ignoring RACH, there is an active talker already.\n"); + return; + } + + /* Set timing advance, power level and activate SACCH */ + lchan->ta_ctrl.current = acc_delay; + lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max; + lchan->want_dl_sacch_active = true; + + /* Stop RACH detection, wait for valid frame */ + lchan->asci.talker_active = VGCS_TALKER_WAIT_FRAME; + if (l1sap_chan_modify(lchan->ts->trx, gsm_lchan2chan_nr(lchan)) != 0) { + LOGPLCHAN(lchan, DASCI, LOGL_ERROR, "failed to modify channel after TALKER DET\n"); + rsl_tx_conn_fail(lchan, RSL_ERR_TALKER_ACC_FAIL); + lchan->asci.talker_active = VGCS_TALKER_NONE; + return; + } + + lchan->asci.ref = ra; + lchan->asci.fn = fn; + + /* Send TALKER DETECT via RSL to BSC */ + rsl_tx_talker_det(lchan, &acc_delay); + + /* Send VGCS UPLINK GRANT */ + lchan->asci.vgcs_ul_grant_count = 1; + tx_vgcs_ul_grant(lchan); + + /* Start T3115 */ + LOGPLCHAN(lchan, DASCI, LOGL_DEBUG, "Starting T3115 with %u ms\n", bts->t3115_ms); + lchan->asci.t3115.cb = vgcs_t3115_cb; + lchan->asci.t3115.data = lchan; + osmo_timer_schedule(&lchan->asci.t3115, 0, bts->t3115_ms * 1000); + } +} + +/* Received channel reactivation. (for assignment) */ +void vgcs_lchan_react(struct gsm_lchan *lchan) +{ + LOGPLCHAN(lchan, DASCI, LOGL_INFO, "Channel is activated for assignment.\n"); + lchan->asci.talker_active = VGCS_TALKER_WAIT_FRAME; + radio_link_timeout_reset(lchan); +} + +/* Received first valid data frame on dedicated channel. */ +void vgcs_talker_frame(struct gsm_lchan *lchan) +{ + LOGPLCHAN(lchan, DASCI, LOGL_INFO, "First valid frame detected, talker now active.\n"); + osmo_timer_del(&lchan->asci.t3115); + lchan->asci.talker_active = VGCS_TALKER_ACTIVE; + radio_link_timeout_reset(lchan); +} + +/* Release VGCS Talker state. */ +void vgcs_talker_reset(struct gsm_lchan *lchan) +{ + if (lchan->asci.talker_active == VGCS_TALKER_NONE) + return; + + LOGPLCHAN(lchan, DASCI, LOGL_INFO, "Uplink released, no talker.\n"); + + /* Stop T3115 */ + osmo_timer_del(&lchan->asci.t3115); + + /* Talker detection done */ + lchan->asci.talker_active = VGCS_TALKER_NONE; +} + +/* Release VGCS Listener state. */ +void vgcs_listener_reset(struct gsm_lchan *lchan) +{ + lchan->asci.listener_detected = false; +} |