laforge has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-bts/+/33289 )

Change subject: ASCI: NCH / NOTIFICATION support
......................................................................

ASCI: NCH / NOTIFICATION support

This adds very minimalistic support for notification of VBS/VGCS calls.

Minimalistic in that we
* only notify via PCH (not via NCH or FACCH)
* only include notification in otherwise empty PAGING TYPE 1

This means that notification will cease to work once the PCH becomes too
loaded and we never would send otherwise empty PAGING TYPE 1 anymore.

Change-Id: I6f6f72d9a0123cb519b341d72a124aaa2117370e
Requires: libosmocore.git I9586b5cb8514010d9358fcfc97c3d34741294522
Related: OS#5781
---
M include/osmo-bts/Makefile.am
M include/osmo-bts/bts.h
A include/osmo-bts/notification.h
M src/common/Makefile.am
M src/common/bts.c
M src/common/nm_bts_fsm.c
A src/common/notification.c
M src/common/paging.c
M src/common/rsl.c
9 files changed, 319 insertions(+), 10 deletions(-)

Approvals:
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified




diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index 0e45d70..1a07287 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -32,5 +32,6 @@
        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 296aa0d..9290089 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -290,6 +290,11 @@
                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 0000000..3d05acc
--- /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 <[email protected]>
+ * 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 32f644c..270139c 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -67,6 +67,7 @@
        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 802cb00..28f7567 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -400,6 +400,8 @@
        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 f929354..86d9307 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 @@
        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 0000000..1fda519
--- /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 <[email protected]>
+ * 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 b3d5d8b..b1aad7c 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 @@
 }

 /* 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 @@
        }
        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 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 @@
                        .data = cur,
                };

-               append_p1_rest_octets(&bv, p1ro);
+               append_p1_rest_octets(&bv, p1ro, notif);
        }

        return GSM_MACBLOCK_LEN;
@@ -582,6 +592,7 @@
 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 @@
        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 @@
                        }
                } 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 d652787..7bd950f 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 @@
        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 @@
        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);

--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/33289
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I6f6f72d9a0123cb519b341d72a124aaa2117370e
Gerrit-Change-Number: 33289
Gerrit-PatchSet: 4
Gerrit-Owner: jolly <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-MessageType: merged

Reply via email to