diff options
-rw-r--r-- | include/osmo-bts/Makefile.am | 1 | ||||
-rw-r--r-- | include/osmo-bts/bts.h | 5 | ||||
-rw-r--r-- | include/osmo-bts/notification.h | 57 | ||||
-rw-r--r-- | src/common/Makefile.am | 1 | ||||
-rw-r--r-- | src/common/bts.c | 2 | ||||
-rw-r--r-- | src/common/nm_bts_fsm.c | 2 | ||||
-rw-r--r-- | src/common/notification.c | 155 | ||||
-rw-r--r-- | src/common/paging.c | 35 | ||||
-rw-r--r-- | src/common/rsl.c | 51 |
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); |