diff options
-rw-r--r-- | include/osmo-bts/bts.h | 13 | ||||
-rw-r--r-- | include/osmo-bts/l1sap.h | 2 | ||||
-rw-r--r-- | include/osmo-bts/notification.h | 2 | ||||
-rw-r--r-- | src/common/bts.c | 34 | ||||
-rw-r--r-- | src/common/l1sap.c | 27 | ||||
-rw-r--r-- | src/common/notification.c | 70 | ||||
-rw-r--r-- | tests/agch/agch_test.c | 2 | ||||
-rw-r--r-- | tests/paging/paging_test.c | 6 |
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"); } |