aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2023-05-31 12:20:55 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2023-07-21 13:20:06 +0200
commit6fa1dfce614da9f9cca642e68402fe5cbaf175d3 (patch)
tree33fbccd422aa8740bdf5c70e92157e623b46dae4
parent65c8f0de946fb61b059bee45abae1372430ddf8b (diff)
ASCI: Add Notification CHannel (NCH) support
The location of the NCH is defined by the rest octet of System Information 1. If NCH is defined, the given CCCH blocks are used for NCH instead of AGCH/PCH. The current list of VGCS/VBS call notifications is transmitted on the NCH. If there is no notification, an empty notification is transmitted on the NCH. The Notification List Number (NLN) is used to indicated new notificaitons. Only the last notification (or empty notification) indicates NLN. This way the MS can determine after two equal NLN that the complete list has been recevied. Change-Id: I82fdaba3faaced76267a99ae14a5458a1b41fdaa Related: OS#5781
-rw-r--r--include/osmo-bts/bts.h13
-rw-r--r--include/osmo-bts/l1sap.h2
-rw-r--r--include/osmo-bts/notification.h2
-rw-r--r--src/common/bts.c34
-rw-r--r--src/common/l1sap.c27
-rw-r--r--src/common/notification.c70
-rw-r--r--tests/agch/agch_test.c2
-rw-r--r--tests/paging/paging_test.c6
8 files changed, 130 insertions, 26 deletions
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index eb0c6637..677ff9c3 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -296,7 +296,10 @@ struct gsm_bts {
/* Advanced Speech Call Items (VBS/VGCS) + NCH related bits */
struct {
int pos_nch; /* position of the NCH or < 0, if not available */
+ uint8_t nln, nln_status; /* current notification list number and status */
struct llist_head notifications;
+ int notification_entries; /* current number of entries in the list */
+ int notification_count; /* counter to count all entries */
} asci;
struct paging_state *paging_state;
@@ -407,8 +410,14 @@ int bts_link_estab(struct gsm_bts *bts);
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
int bts_agch_max_queue_length(int T, int bcch_conf);
-int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
- int is_ag_res);
+
+enum ccch_msgt {
+ CCCH_MSGT_AGCH,
+ CCCH_MSGT_PCH,
+ CCCH_MSGT_NCH,
+};
+
+int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt, enum ccch_msgt ccch);
int bts_supports_cipher(struct gsm_bts *bts, int rsl_cipher);
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, const struct gsm_time *g_time);
void regenerate_si3_restoctets(struct gsm_bts *bts);
diff --git a/include/osmo-bts/l1sap.h b/include/osmo-bts/l1sap.h
index 87e1a990..353fc905 100644
--- a/include/osmo-bts/l1sap.h
+++ b/include/osmo-bts/l1sap.h
@@ -146,6 +146,6 @@ void radio_link_timeout_reset(struct gsm_lchan *lchan);
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len);
-int is_ccch_for_agch(struct gsm_bts_trx *trx, uint32_t fn);
+enum ccch_msgt get_ccch_msgt(struct gsm_bts_trx *trx, uint32_t fn);
#endif /* L1SAP_H */
diff --git a/include/osmo-bts/notification.h b/include/osmo-bts/notification.h
index 3d05acc6..f8a7b480 100644
--- a/include/osmo-bts/notification.h
+++ b/include/osmo-bts/notification.h
@@ -55,3 +55,5 @@ 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);
+
+int bts_asci_notify_nch_gen_msg(struct gsm_bts *bts, uint8_t *out_buf);
diff --git a/src/common/bts.c b/src/common/bts.c
index b4a5f804..7257f37b 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -56,6 +56,7 @@
#include <osmo-bts/nm_common_fsm.h>
#include <osmo-bts/power_control.h>
#include <osmo-bts/osmux.h>
+#include <osmo-bts/notification.h>
#define MIN_QUAL_RACH 50 /* minimum link quality (in centiBels) for Access Bursts */
#define MIN_QUAL_NORM -5 /* minimum link quality (in centiBels) for Normal Bursts */
@@ -723,8 +724,7 @@ static void compact_agch_queue(struct gsm_bts *bts)
return;
}
-int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
- int is_ag_res)
+int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt, enum ccch_msgt ccch)
{
struct msgb *msg = NULL;
int rc = 0;
@@ -737,17 +737,25 @@ int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt
*/
compact_agch_queue(bts);
- /* Check for paging messages first if this is PCH */
- if (!is_ag_res)
- rc = paging_gen_msg(bts->paging_state, out_buf, gt, &is_empty);
-
- /* Check whether the block may be overwritten */
- if (!is_empty)
- return rc;
-
- msg = bts_agch_dequeue(bts);
- if (!msg)
+ switch (ccch) {
+ case CCCH_MSGT_NCH:
+ /* Send NCH message, it has priority over AGCH and does not overlap with PCH. */
+ rc = bts_asci_notify_nch_gen_msg(bts, out_buf);
return rc;
+ case CCCH_MSGT_PCH:
+ /* Check whether the block may be overwritten by AGCH. */
+ rc = paging_gen_msg(bts->paging_state, out_buf, gt, &is_empty);
+ if (!is_empty)
+ return rc;
+ /* fall-through */
+ case CCCH_MSGT_AGCH:
+ /* If fallen here and the AGCH queue is empty, return empty PCH message. */
+ msg = bts_agch_dequeue(bts);
+ if (!msg)
+ return rc;
+ /* Continue to return AGCH message. */
+ break;
+ }
rate_ctr_inc2(bts->ctrs, BTS_CTR_AGCH_SENT);
@@ -756,7 +764,7 @@ int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt
rc = msgb_l3len(msg);
msgb_free(msg);
- if (is_ag_res)
+ if (ccch == CCCH_MSGT_AGCH)
bts->agch_queue.agch_msgs++;
else
bts->agch_queue.pch_msgs++;
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 1c43f8ff..268486e0 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -902,15 +902,32 @@ static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan,
return 0;
}
-/* Check if given CCCH frame number is for a PCH or for an AGCH (this function is
+/* Check if given CCCH frame number is for a NCH, PCH or for an AGCH (this function is
* only used internally, it is public to call it from unit-tests) */
-int is_ccch_for_agch(struct gsm_bts_trx *trx, uint32_t fn) {
+enum ccch_msgt get_ccch_msgt(struct gsm_bts_trx *trx, uint32_t fn)
+{
+ uint8_t block, first_block, num_blocks;
+ int rc;
+
+ block = l1sap_fn2ccch_block(fn);
+
+ /* If there is an NCH, check if the block number matches. It has priority over PCH/AGCH. */
+ if (trx->bts->asci.pos_nch >= 0) {
+ rc = osmo_gsm48_si1ro_nch_pos_decode(trx->bts->asci.pos_nch, &num_blocks, &first_block);
+ if (rc >= 0 && block >= first_block && block < first_block + num_blocks)
+ return CCCH_MSGT_NCH;
+ }
+
/* Note: The number of available access grant channels is set by the
* parameter BS_AG_BLKS_RES via system information type 3. This SI is
* transferred to osmo-bts via RSL */
- return l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND");
+ if (l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND"))
+ return CCCH_MSGT_AGCH;
+
+ return CCCH_MSGT_PCH;
}
+
/* return the measured average of frame numbers that the RTS clock is running in advance */
int32_t bts_get_avg_fn_advance(const struct gsm_bts *bts)
{
@@ -1048,7 +1065,6 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
struct msgb *pp_msg;
bool dtxd_facch = false;
int rc;
- int is_ag_res;
chan_nr = rts_ind->chan_nr;
link_id = rts_ind->link_id;
@@ -1183,8 +1199,7 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
}
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
- is_ag_res = is_ccch_for_agch(trx, fn);
- rc = bts_ccch_copy_msg(trx->bts, p, &g_time, is_ag_res);
+ rc = bts_ccch_copy_msg(trx->bts, p, &g_time, get_ccch_msgt(trx, fn));
if (rc <= 0)
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
}
diff --git a/src/common/notification.c b/src/common/notification.c
index 1fda5194..35dc65f9 100644
--- a/src/common/notification.c
+++ b/src/common/notification.c
@@ -71,6 +71,10 @@ int bts_asci_notification_add(struct gsm_bts *bts, const uint8_t *group_call_ref
/* add at beginning of "queue" to make sure a new call is notified first */
llist_add(&n->list, &bts->asci.notifications);
+ bts->asci.notification_entries++;
+ bts->asci.notification_count = 0;
+ bts->asci.nln = (bts->asci.nln + 1) % 4;
+
return 0;
}
@@ -86,6 +90,10 @@ int bts_asci_notification_del(struct gsm_bts *bts, const uint8_t *group_call_ref
llist_del(&n->list);
talloc_free(n);
+ bts->asci.notification_entries--;
+ bts->asci.notification_count = 0;
+ bts->asci.nln_status = (bts->asci.nln_status + 1) % 2;
+
return 0;
}
@@ -100,6 +108,11 @@ int bts_asci_notification_reset(struct gsm_bts *bts)
llist_del(&n->list);
talloc_free(n);
}
+
+ bts->asci.notification_entries = 0;
+ bts->asci.notification_count = 0;
+ bts->asci.nln_status = (bts->asci.nln_status + 1) % 2;
+
return 0;
}
@@ -153,3 +166,60 @@ void append_group_call_information(struct bitvec *bv, const uint8_t *gcr, const
bitvec_free(gcr_bv);
}
+
+#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
+
+int bts_asci_notify_nch_gen_msg(struct gsm_bts *bts, uint8_t *out_buf)
+{
+ struct gsm48_notification_nch *nn = (struct gsm48_notification_nch *) out_buf;
+ const struct asci_notification *notif;
+ unsigned int ro_len;
+
+ notif = bts_asci_notification_get_next(bts);
+
+ *nn = (struct gsm48_notification_nch) {
+ .proto_discr = GSM48_PDISC_RR,
+ .msg_type = GSM48_MT_RR_NOTIF_NCH,
+ };
+
+ nn->l2_plen = L2_PLEN(nn->data - out_buf);
+
+ /* Pad remaining octets with constant '2B'O */
+ ro_len = GSM_MACBLOCK_LEN - (nn->data - out_buf);
+ memset(nn->data, GSM_MACBLOCK_PADDING, ro_len);
+
+ struct bitvec bv = {
+ .data_len = ro_len,
+ .data = nn->data,
+ };
+
+ /* {0 | 1 < NLN(NCH) : bit (2) >}
+ * Only send NLN, at the last notifications.
+ * When the phone receives two NLN with the same value, it knows that all notifications has been received.
+ * Also send NLN if no notification is available. */
+ if (bts->asci.notification_count >= bts->asci.notification_entries - 1) {
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, bts->asci.nln, 2);
+ } else {
+ bitvec_set_bit(&bv, 0);
+ }
+
+ /* Count NLN. */
+ if (++bts->asci.notification_count >= bts->asci.notification_entries)
+ bts->asci.notification_count = 0;
+
+ /* < List of Group Call NCH information > ::=
+ * { 0 | 1 < Group Call information > < List of Group Call NCH information > } ; */
+ if (notif) {
+ bitvec_set_bit(&bv, 1);
+ append_group_call_information(&bv, notif->group_call_ref,
+ notif->chan_desc.present ? notif->chan_desc.value : NULL,
+ notif->chan_desc.len);
+ }
+ bitvec_set_bit(&bv, 0); /* End of list */
+
+ /* TODO: Additions in Release 6 */
+ /* TODO: Additions in Release 7 */
+
+ return GSM_MACBLOCK_LEN;
+}
diff --git a/tests/agch/agch_test.c b/tests/agch/agch_test.c
index 8ddc0f7d..86c5c203 100644
--- a/tests/agch/agch_test.c
+++ b/tests/agch/agch_test.c
@@ -159,7 +159,7 @@ static void test_agch_queue(void)
if (is_agch)
multiframes++;
- rc = bts_ccch_copy_msg(bts, out_buf, &g_time, is_agch);
+ rc = bts_ccch_copy_msg(bts, out_buf, &g_time, (is_agch) ? CCCH_MSGT_AGCH : CCCH_MSGT_PCH);
ima = (struct gsm48_imm_ass *)out_buf;
switch (ima->msg_type) {
case GSM48_MT_RR_IMM_ASS:
diff --git a/tests/paging/paging_test.c b/tests/paging/paging_test.c
index 26f5728a..d48932bb 100644
--- a/tests/paging/paging_test.c
+++ b/tests/paging/paging_test.c
@@ -144,7 +144,7 @@ static struct gsm_bts_trx *test_is_ccch_for_agch_setup(uint8_t bs_ag_blks_res)
* Table 5 of 9 must occur. */
static void test_is_ccch_for_agch(void)
{
- int is_ag_res;
+ enum ccch_msgt ccch;
int fn;
uint8_t bs_ag_blks_res;
struct gsm_bts_trx *trx;
@@ -172,8 +172,8 @@ static void test_is_ccch_for_agch(void)
/* Try allo possible settings for bs_ag_blks_res */
for (bs_ag_blks_res = 0; bs_ag_blks_res <= 7; bs_ag_blks_res++) {
trx = test_is_ccch_for_agch_setup(bs_ag_blks_res);
- is_ag_res = is_ccch_for_agch(trx, fn);
- printf(" %u", is_ag_res);
+ ccch = get_ccch_msgt(trx, fn);
+ printf(" %u", (ccch == CCCH_MSGT_AGCH));
}
printf("\n");
}