aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-bts/Makefile.am1
-rw-r--r--include/osmo-bts/bts.h5
-rw-r--r--include/osmo-bts/notification.h57
-rw-r--r--src/common/Makefile.am1
-rw-r--r--src/common/bts.c2
-rw-r--r--src/common/nm_bts_fsm.c2
-rw-r--r--src/common/notification.c155
-rw-r--r--src/common/paging.c35
-rw-r--r--src/common/rsl.c51
9 files changed, 299 insertions, 10 deletions
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index 0e45d708..1a072871 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -32,5 +32,6 @@ noinst_HEADERS = \
dtx_dl_amr_fsm.h \
ta_control.h \
nm_common_fsm.h \
+ notification.h \
osmux.h \
$(NULL)
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index 296aa0d6..92900890 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -290,6 +290,11 @@ struct gsm_bts {
bool pni; /* Primary Notification Identifier */
} etws;
+ /* Advanced Speech Call Items (VBS/VGCS) + NCH related bits */
+ struct {
+ struct llist_head notifications;
+ } asci;
+
struct paging_state *paging_state;
struct llist_head bsc_oml_hosts;
unsigned int rtp_jitter_buf_ms;
diff --git a/include/osmo-bts/notification.h b/include/osmo-bts/notification.h
new file mode 100644
index 00000000..3d05acc6
--- /dev/null
+++ b/include/osmo-bts/notification.h
@@ -0,0 +1,57 @@
+/* Maintain and generate ASCI notifications */
+
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Harald Welte
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+/* one [concurrent] ASCI (VBS/VGCS) notification */
+struct asci_notification {
+ struct llist_head list; /* linked to bts->asci.notifications */
+
+ /* Group call reference (TS 24.008 10.5.1.9 "Descriptive group or broadcast call reference") */
+ uint8_t group_call_ref[5];
+
+ /* Group Channel Description (TS 44.018 10.5.2.14b) */
+ struct {
+ bool present;
+ uint8_t value[255];
+ uint8_t len;
+ } chan_desc;
+
+ /* NCH DRX Information (TS 48.058 9.3.47) */
+ struct {
+ bool present;
+ struct rsl_ie_nch_drx_info value;
+ } nch_drx_info;
+};
+
+int bts_asci_notification_add(struct gsm_bts *bts, const uint8_t *group_call_ref, const uint8_t *chan_desc,
+ uint8_t chan_desc_len, const struct rsl_ie_nch_drx_info *nch_drx_info);
+
+int bts_asci_notification_del(struct gsm_bts *bts, const uint8_t *group_call_ref);
+
+int bts_asci_notification_reset(struct gsm_bts *bts);
+
+const struct asci_notification *bts_asci_notification_get_next(struct gsm_bts *bts);
+
+void append_group_call_information(struct bitvec *bv, const uint8_t *gcr, const uint8_t *ch_desc, uint8_t ch_desc_len);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 32f644c3..270139c9 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -67,6 +67,7 @@ libbts_a_SOURCES = \
nm_gprs_nse_fsm.c \
nm_gprs_nsvc_fsm.c \
nm_radio_carrier_fsm.c \
+ notification.c \
probes.d \
$(NULL)
diff --git a/src/common/bts.c b/src/common/bts.c
index 802cb008..28f75678 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -400,6 +400,8 @@ int bts_init(struct gsm_bts *bts)
bts->smscb_queue_tgt_len = 2;
bts->smscb_queue_hyst = 2;
+ INIT_LLIST_HEAD(&bts->asci.notifications);
+
INIT_LLIST_HEAD(&bts->bsc_oml_hosts);
/* register DTX DL FSM */
diff --git a/src/common/nm_bts_fsm.c b/src/common/nm_bts_fsm.c
index f9293540..86d93073 100644
--- a/src/common/nm_bts_fsm.c
+++ b/src/common/nm_bts_fsm.c
@@ -36,6 +36,7 @@
#include <osmo-bts/nm_common_fsm.h>
#include <osmo-bts/phy_link.h>
#include <osmo-bts/cbch.h>
+#include <osmo-bts/notification.h>
#define X(s) (1 << (s))
@@ -64,6 +65,7 @@ static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint3
bts->bsic = 0xff; /* invalid value */
TALLOC_FREE(bts->mo.nm_attr);
bts_cbch_reset(bts);
+ bts_asci_notification_reset(bts);
if (bts->c0_power_red_db > 0)
bts_set_c0_pwr_red(bts, 0);
diff --git a/src/common/notification.c b/src/common/notification.c
new file mode 100644
index 00000000..1fda5194
--- /dev/null
+++ b/src/common/notification.c
@@ -0,0 +1,155 @@
+/* Maintain and generate ASCI notifications */
+
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Harald Welte
+ *
+ * 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 <errno.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/notification.h>
+
+static struct asci_notification *bts_asci_notification_find(struct gsm_bts *bts, const uint8_t *group_call_ref)
+{
+ struct asci_notification *n;
+ llist_for_each_entry(n, &bts->asci.notifications, list) {
+ if (!memcmp(n->group_call_ref, group_call_ref, sizeof(n->group_call_ref)))
+ return n;
+ }
+ return NULL;
+}
+
+int bts_asci_notification_add(struct gsm_bts *bts, const uint8_t *group_call_ref, const uint8_t *chan_desc,
+ uint8_t chan_desc_len, const struct rsl_ie_nch_drx_info *nch_drx_info)
+{
+ struct asci_notification *n;
+
+ if (bts_asci_notification_find(bts, group_call_ref))
+ return -EEXIST;
+
+ n = talloc_zero(bts, struct asci_notification);
+ if (!n)
+ return -ENOMEM;
+
+ memcpy(n->group_call_ref, group_call_ref, sizeof(n->group_call_ref));
+ if (chan_desc && chan_desc_len) {
+ n->chan_desc.present = true;
+ n->chan_desc.len = chan_desc_len;
+ memcpy(&n->chan_desc.value, chan_desc, chan_desc_len);
+ }
+ if (nch_drx_info) {
+ n->nch_drx_info.present = true;
+ n->nch_drx_info.value = *nch_drx_info;
+ }
+
+ LOGP(DASCI, LOGL_INFO, "Added ASCI Notification for group call reference %s\n",
+ osmo_hexdump_nospc(n->group_call_ref, ARRAY_SIZE(n->group_call_ref)));
+
+ /* add at beginning of "queue" to make sure a new call is notified first */
+ llist_add(&n->list, &bts->asci.notifications);
+
+ return 0;
+}
+
+int bts_asci_notification_del(struct gsm_bts *bts, const uint8_t *group_call_ref)
+{
+ struct asci_notification *n = bts_asci_notification_find(bts, group_call_ref);
+ if (!n)
+ return -ENODEV;
+
+ LOGP(DASCI, LOGL_INFO, "Deleting ASCI Notification for group call reference %s\n",
+ osmo_hexdump_nospc(n->group_call_ref, ARRAY_SIZE(n->group_call_ref)));
+
+ llist_del(&n->list);
+ talloc_free(n);
+
+ return 0;
+}
+
+int bts_asci_notification_reset(struct gsm_bts *bts)
+{
+ struct asci_notification *n, *n2;
+
+ LOGP(DASCI, LOGL_INFO, "Deleting all %u ASCI Notifications of BTS\n",
+ llist_count(&bts->asci.notifications));
+
+ llist_for_each_entry_safe(n, n2, &bts->asci.notifications, list) {
+ llist_del(&n->list);
+ talloc_free(n);
+ }
+ return 0;
+}
+
+const struct asci_notification *bts_asci_notification_get_next(struct gsm_bts *bts)
+{
+ struct asci_notification *n;
+
+ n = llist_first_entry_or_null(&bts->asci.notifications, struct asci_notification, list);
+ if (!n)
+ return NULL;
+
+ /* move to end of list to iterate over them */
+ llist_del(&n->list);
+ llist_add_tail(&n->list, &bts->asci.notifications);
+
+ return n;
+}
+
+
+/*! append a "Group Call Information" CSN.1 structure to the caller-provided bit-vector.
+ * \param[out] bv caller-provided output bit-vector
+ * \param[in] gcr 5-byte group call reference
+ * \param[in] ch_desc optional group channel description (may be NULL)
+ * \param[in] ch_desc_len length of group channel description (in bytes) */
+void append_group_call_information(struct bitvec *bv, const uint8_t *gcr, const uint8_t *ch_desc, uint8_t ch_desc_len)
+{
+ /* spec reference: TS 44.018 Section 9.1.21a */
+
+ /* <Group Call Reference : bit(36)> */
+ struct bitvec *gcr_bv = bitvec_alloc(5*8, NULL);
+ OSMO_ASSERT(gcr_bv);
+ bitvec_unpack(gcr_bv, gcr);
+ for (unsigned int i = 0; i < 36; i++)
+ bitvec_set_bit(bv, bitvec_get_bit_pos(gcr_bv, i));
+
+ /* Group Channel Description */
+ if (ch_desc && ch_desc_len) {
+ struct bitvec *chd_bv = bitvec_alloc(ch_desc_len*8, NULL);
+ OSMO_ASSERT(chd_bv);
+ bitvec_unpack(chd_bv, ch_desc);
+ bitvec_set_bit(bv, 1);
+ /* <Channel Description : bit(24)> */
+ for (unsigned int i = 0; i < ch_desc_len * 8; i++)
+ bitvec_set_bit(bv, bitvec_get_bit_pos(chd_bv, i));
+ bitvec_free(chd_bv);
+ /* FIXME: hopping */
+ bitvec_set_bit(bv, 0);
+ } else {
+ bitvec_set_bit(bv, 0);
+ }
+
+ bitvec_free(gcr_bv);
+}
diff --git a/src/common/paging.c b/src/common/paging.c
index b3d5d8be..b1aad7c3 100644
--- a/src/common/paging.c
+++ b/src/common/paging.c
@@ -43,6 +43,7 @@
#include <osmo-bts/paging.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/pcu_if.h>
+#include <osmo-bts/notification.h>
#define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 9
@@ -366,7 +367,8 @@ static void append_etws_prim_notif(struct bitvec *bv, bool is_first, uint8_t pag
}
/* 3GPP TS 44.018 10.5.2.23 append P1 Rest Octets to given bit-vector */
-static void append_p1_rest_octets(struct bitvec *bv, const struct p1_rest_octets *p1ro)
+static void append_p1_rest_octets(struct bitvec *bv, const struct p1_rest_octets *p1ro,
+ const struct asci_notification *notif)
{
/* Paging 1 RO (at least 10 bits before ETWS struct) */
if (p1ro->nln_pch.present) {
@@ -378,7 +380,14 @@ static void append_p1_rest_octets(struct bitvec *bv, const struct p1_rest_octets
}
bitvec_set_bit(bv, L); /* no Priority1 */
bitvec_set_bit(bv, L); /* no Priority2 */
- bitvec_set_bit(bv, L); /* no Group Call Info */
+ if (notif) {
+ bitvec_set_bit(bv, H); /* Group Call Info */
+ append_group_call_information(bv, notif->group_call_ref,
+ notif->chan_desc.present ? notif->chan_desc.value : NULL,
+ notif->chan_desc.len);
+ } else {
+ bitvec_set_bit(bv, L); /* no Group Call Info */
+ }
if (p1ro->packet_page_ind[0])
bitvec_set_bit(bv, H); /* Packet Page Indication 1 */
else
@@ -405,7 +414,8 @@ static void append_p1_rest_octets(struct bitvec *bv, const struct p1_rest_octets
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
uint8_t chan1, const uint8_t *identity2_lv,
- uint8_t chan2, const struct p1_rest_octets *p1ro)
+ uint8_t chan2, const struct p1_rest_octets *p1ro,
+ const struct asci_notification *notif)
{
struct gsm48_paging1 *pt1 = (struct gsm48_paging1 *) out_buf;
unsigned int ro_len;
@@ -436,7 +446,7 @@ static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
.data = cur,
};
- append_p1_rest_octets(&bv, p1ro);
+ append_p1_rest_octets(&bv, p1ro, notif);
}
return GSM_MACBLOCK_LEN;
@@ -582,6 +592,7 @@ static void build_p1_rest_octets(struct p1_rest_octets *p1ro, struct gsm_bts *bt
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
int *is_empty)
{
+ const struct asci_notification *notif;
struct llist_head *group_q;
struct gsm_bts *bts = ps->bts;
int group;
@@ -607,12 +618,16 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
if (ps->bts->etws.prim_notif) {
struct p1_rest_octets p1ro;
build_p1_rest_octets(&p1ro, bts);
- len = fill_paging_type_1(out_buf, empty_id_lv, 0, NULL, 0, &p1ro);
+ /* we intentioanally don't try to add notifications here, as ETWS is more critical */
+ len = fill_paging_type_1(out_buf, empty_id_lv, 0, NULL, 0, &p1ro, NULL);
} else if (llist_empty(group_q)) {
/* There is nobody to be paged, send Type1 with two empty ID */
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
- len = fill_paging_type_1(out_buf, empty_id_lv, 0,
- NULL, 0, NULL);
+ /* for now, we only send VGCS/VBS notfication if we have nothing else to page;
+ * this is the safe choice. For other situations with mobile identities in the
+ * paging type 1, we'd need to check if there's sufficient space in the rest octets. */
+ notif = bts_asci_notification_get_next(bts);
+ len = fill_paging_type_1(out_buf, empty_id_lv, 0, NULL, 0, NULL, notif);
*is_empty = 1;
} else {
struct paging_record *pr[4];
@@ -688,19 +703,21 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
}
} else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
+ /* TODO: check if we can include an ASCI notification */
len = fill_paging_type_1(out_buf,
pr[0]->u.normal.identity_lv,
pr[0]->u.normal.chan_needed,
- NULL, 0, NULL);
+ NULL, 0, NULL, NULL);
} else {
/* 2 (any type) or
* 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
+ /* TODO: check if we can include an ASCI notification */
len = fill_paging_type_1(out_buf,
pr[0]->u.normal.identity_lv,
pr[0]->u.normal.chan_needed,
pr[1]->u.normal.identity_lv,
- pr[1]->u.normal.chan_needed, NULL);
+ pr[1]->u.normal.chan_needed, NULL, NULL);
if (num_pr >= 3) {
/* re-add #4 for next time */
llist_add(&pr[2]->list, group_q);
diff --git a/src/common/rsl.c b/src/common/rsl.c
index d652787a..7bd950f3 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -56,6 +56,7 @@
#include <osmo-bts/l1sap.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcuif_proto.h>
+#include <osmo-bts/notification.h>
//#define FAKE_CIPH_MODE_COMPL
@@ -775,6 +776,52 @@ static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
return 0;
}
+/* 8.5.10 NOTIFICATION COMMAND */
+static int rsl_rx_notification_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
+ struct tlv_parsed tp;
+ uint8_t command_indicator;
+ int rc;
+
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
+
+ if (cch->chan_nr != RSL_CHAN_PCH_AGCH) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): chan nr is not Downlink CCCH\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg);
+ }
+
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_CMD_INDICATOR, 1))
+ return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg);
+ command_indicator = *TLVP_VAL(&tp, RSL_IE_CMD_INDICATOR);
+
+ switch (command_indicator) {
+ case RSL_CMD_INDICATOR_START:
+ /* we need at least a Group Call Reference to start notification */
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5))
+ return rsl_tx_error_report(trx, RSL_ERR_OPT_IE_ERROR, &cch->chan_nr, NULL, msg);
+ rc = bts_asci_notification_add(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF),
+ TLVP_VAL(&tp, RSL_IE_CHAN_DESC), TLVP_LEN(&tp, RSL_IE_CHAN_DESC),
+ (struct rsl_ie_nch_drx_info *) TLVP_VAL(&tp, RSL_IE_NCH_DRX_INFO));
+ break;
+ case RSL_CMD_INDICATOR_STOP:
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5)) {
+ /* interpret this as stopping of all notification */
+ rc = bts_asci_notification_reset(trx->bts);
+ } else {
+ rc = bts_asci_notification_del(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF));
+ }
+ break;
+ default:
+ return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg);
+ }
+
+ return rc;
+}
+
/* OSMO_ETWS_CMD - proprietary extension as TS 48.058 has no standardized way to do this :( */
static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
{
@@ -3680,8 +3727,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
case RSL_MT_SMS_BC_CMD:
ret = rsl_rx_sms_bcast_cmd(trx, msg);
break;
- case RSL_MT_SMS_BC_REQ:
case RSL_MT_NOT_CMD:
+ ret = rsl_rx_notification_cmd(trx, msg);
+ break;
+ case RSL_MT_SMS_BC_REQ:
LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n",
rsl_msg_name(cch->c.msg_type));
rsl_tx_error_report(trx, RSL_ERR_MSG_TYPE, &cch->chan_nr, NULL, msg);