Merge remote-tracking branch 'origin/nr-paging-cu' into integration_2026_w08 (!3893)
NR Paging on CU
Implements the NR paging path on the CU:
- NGAP Paging is decoded and passed to RRC
- RRC builds F1AP Paging, and distributes it to DUs via F1 callback
Changes:
- Refactor and extend F1AP Paging encode/decode libraries
* add missing enc/dec/eq/cp functions
* add unit test
* IEs: CN UE 5G-S-TMSI, RAN UE I-RNTI; optional DRX/priority/origin)
- Refactor CU callback CU_send_Paging
- Refactor DU handler DU_handle_Paging
- Refactor old F1 callback for Paging indication, extend it and align
with existing F1 callbacks in stack
- Refactor and extend NGAP Paging encode/decode lib
* add missing enc/dec/eq/cp functions
* add unit test
* Fixes NGAP paging to accept Paging only on SCTP stream 0 per TS
38.412
- Includes supporting refactors (PDU session split, shared 5G-S-TMSI
helpers, NGAP/F1AP cleanups)
Testing
- NGAP and F1AP Paging lib unit tests (enc/dec)
- Stream-0 fix validated with Open5GS AMF
This MR does not address the DU changes besides the F1 handler. F1AP
Paging does not have an RRC container to pass the PCCH message to the
DU, and F1 DL RRC Message Transfer does not apply to PCCH. PCCH-Message
shall be built at DU per TS 38.331 §5.3.2, with padding as per §8.5 and
delivered as RLC SDU per §8.2. For each cell in Paging Cell List that
belongs to this DU, queue for MAC. MAC schedules at PF/PO per TS 38.304
§7.
Refs:
- F1AP 3GPP TS 38.473 version 16.21.0 Release 16
- NGAP 3GPP TS 38.413 version 16.17.0 Release 16
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef EQ_CHECK_H_
|
||||
#define EQ_CHECK_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Layers may override PRINT_ERROR() before including this header to preserve
|
||||
* custom logging style (for example by mapping it to a local PRINT_ERROR()).
|
||||
*/
|
||||
|
||||
#ifndef PRINT_ERROR
|
||||
#ifdef ENABLE_TESTS
|
||||
#define PRINT_ERROR(fmt, ...) fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_ERROR(...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EQ_CHECK_FAIL
|
||||
#define EQ_CHECK_FAIL() return false
|
||||
#endif
|
||||
|
||||
#define EQ_CHECK_GENERIC(condition, fmt, ...) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
PRINT_ERROR("Equality condition '%s' failed: " fmt " != " fmt "\n", #condition, ##__VA_ARGS__); \
|
||||
EQ_CHECK_FAIL(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define _EQ_CHECK_LONG(A, B) EQ_CHECK_GENERIC((A) == (B), "%ld", (A), (B))
|
||||
#define _EQ_CHECK_INT(A, B) EQ_CHECK_GENERIC((A) == (B), "%d", (A), (B))
|
||||
#define _EQ_CHECK_UINT32(A, B) EQ_CHECK_GENERIC((A) == (B), "%u", (A), (B))
|
||||
#define _EQ_CHECK_UINT64(A, B) EQ_CHECK_GENERIC((A) == (B), "%" PRIu64, (uint64_t)(A), (uint64_t)(B))
|
||||
#define _EQ_CHECK_STR(A, B) EQ_CHECK_GENERIC(strcmp((A), (B)) == 0, "'%s'", (A), (B))
|
||||
|
||||
#define _EQ_CHECK_OPTIONAL_IE(A, B, FIELD, EQ_MACRO) \
|
||||
do { \
|
||||
EQ_CHECK_GENERIC((((A)->FIELD) != NULL) == (((B)->FIELD) != NULL), "%d", ((A)->FIELD) != NULL, ((B)->FIELD) != NULL); \
|
||||
if ((A)->FIELD && (B)->FIELD) \
|
||||
EQ_MACRO(*(A)->FIELD, *(B)->FIELD); \
|
||||
} while (0)
|
||||
|
||||
#define _EQ_CHECK_OPTIONAL_PTR(A, B, FIELD) \
|
||||
do { \
|
||||
if ((((A)->FIELD) != NULL) != (((B)->FIELD) != NULL)) { \
|
||||
PRINT_ERROR("%s:%d: optional IE '%s' not allocated: %p, %p\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
#FIELD, \
|
||||
(void *)(A)->FIELD, \
|
||||
(void *)(B)->FIELD); \
|
||||
EQ_CHECK_FAIL(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* EQ_CHECK_H_ */
|
||||
@@ -1463,6 +1463,18 @@ uint64_t nr_construct_5g_s_tmsi(uint16_t amf_set_id, uint8_t amf_pointer, uint32
|
||||
return ((uint64_t)amf_set_id << 38) | ((uint64_t)amf_pointer << 32) | m_tmsi;
|
||||
}
|
||||
|
||||
/** @brief Extract 5G-S-TMSI-Part1 from full 5G-S-TMSI
|
||||
* @param fiveg_s_tmsi Full 5G-S-TMSI (48 bits)
|
||||
* @return 5G-S-TMSI-Part1 (rightmost 39 bits)
|
||||
* @note 5G-S-TMSI-Part1 is the rightmost 39 bits of the full 5G-S-TMSI:
|
||||
* - Bits 32-37: AMF Pointer (6 bits)
|
||||
* - Bits 0-31: 5G-TMSI (32 bits)
|
||||
* @ref 3GPP TS 23.003 */
|
||||
uint64_t nr_extract_5g_s_tmsi_part1(const uint64_t fiveg_s_tmsi)
|
||||
{
|
||||
return fiveg_s_tmsi & ((1ULL << 39) - 1);
|
||||
}
|
||||
|
||||
/** @brief Construct 5G-S-TMSI-Part1 from 5G-S-TMSI components
|
||||
* @param amf_set_id AMF Set ID (10 bits)
|
||||
* @param amf_pointer AMF Pointer (6 bits)
|
||||
@@ -1476,5 +1488,48 @@ uint64_t nr_construct_5g_s_tmsi_part1(uint16_t amf_set_id, uint8_t amf_pointer,
|
||||
{
|
||||
// Construct full 5G-S-TMSI and extract Part1: rightmost 39 bits
|
||||
uint64_t full_s_tmsi = nr_construct_5g_s_tmsi(amf_set_id, amf_pointer, m_tmsi);
|
||||
return full_s_tmsi & ((1ULL << 39) - 1);
|
||||
return nr_extract_5g_s_tmsi_part1(full_s_tmsi);
|
||||
}
|
||||
|
||||
/** @brief Extract 5G-S-TMSI-Part2 from full 5G-S-TMSI
|
||||
* @param fiveg_s_tmsi Full 5G-S-TMSI (48 bits)
|
||||
* @return 5G-S-TMSI-Part2 (leftmost 9 bits)
|
||||
* @note 5G-S-TMSI-Part2 is the leftmost 9 bits of the full 5G-S-TMSI:
|
||||
* - Bits 39-47: AMF Set ID (9 bits)
|
||||
* @ref 3GPP TS 23.003 */
|
||||
uint16_t nr_extract_5g_s_tmsi_part2(const uint64_t fiveg_s_tmsi)
|
||||
{
|
||||
return (fiveg_s_tmsi >> 39) & ((1ULL << 9) - 1);
|
||||
}
|
||||
|
||||
/** @brief Build full 5G-S-TMSI from Part1 and Part2
|
||||
* @param part1 5G-S-TMSI-Part1 (rightmost 39 bits)
|
||||
* @param part2 5G-S-TMSI-Part2 (leftmost 9 bits)
|
||||
* @return Full 5G-S-TMSI (48 bits)
|
||||
* @note Combines Part2 (leftmost 9 bits) and Part1 (rightmost 39 bits)
|
||||
* @ref 3GPP TS 23.003 */
|
||||
uint64_t nr_build_full_5g_s_tmsi(const uint64_t part1, const uint16_t part2)
|
||||
{
|
||||
return ((uint64_t)part2 << 39) | part1;
|
||||
}
|
||||
|
||||
/** @brief Deconstruct full 5G-S-TMSI into its components
|
||||
* @param fiveg_s_tmsi Full 5G-S-TMSI (48 bits)
|
||||
* @param[out] amf_set_id Pointer to store AMF Set ID (10 bits)
|
||||
* @param[out] amf_pointer Pointer to store AMF Pointer (6 bits)
|
||||
* @param[out] m_tmsi Pointer to store 5G-TMSI (32 bits)
|
||||
* @note The 5G-S-TMSI is deconstructed from a 48-bit value:
|
||||
* - Bits 38-47: AMF Set ID (10 bits)
|
||||
* - Bits 32-37: AMF Pointer (6 bits)
|
||||
* - Bits 0-31: 5G-TMSI (32 bits)
|
||||
* @ref 3GPP TS 23.003 */
|
||||
void nr_deconstruct_5g_s_tmsi(const uint64_t fiveg_s_tmsi, uint16_t *amf_set_id, uint8_t *amf_pointer, uint32_t *m_tmsi)
|
||||
{
|
||||
DevAssert(amf_set_id != NULL);
|
||||
DevAssert(amf_pointer != NULL);
|
||||
DevAssert(m_tmsi != NULL);
|
||||
|
||||
*amf_set_id = fiveg_s_tmsi >> 38;
|
||||
*amf_pointer = (fiveg_s_tmsi >> 32) & 0x3F;
|
||||
*m_tmsi = fiveg_s_tmsi;
|
||||
}
|
||||
|
||||
@@ -360,6 +360,18 @@ uint64_t nr_construct_5g_s_tmsi(uint16_t amf_set_id, uint8_t amf_pointer, uint32
|
||||
/** @brief Construct 5G-S-TMSI-Part1 from 5G-S-TMSI components */
|
||||
uint64_t nr_construct_5g_s_tmsi_part1(uint16_t amf_set_id, uint8_t amf_pointer, uint32_t m_tmsi);
|
||||
|
||||
/** @brief Extract 5G-S-TMSI-Part1 from full 5G-S-TMSI */
|
||||
uint64_t nr_extract_5g_s_tmsi_part1(const uint64_t fiveg_s_tmsi);
|
||||
|
||||
/** @brief Extract 5G-S-TMSI-Part2 from full 5G-S-TMSI */
|
||||
uint16_t nr_extract_5g_s_tmsi_part2(const uint64_t fiveg_s_tmsi);
|
||||
|
||||
/** @brief Build full 5G-S-TMSI from Part1 and Part2 */
|
||||
uint64_t nr_build_full_5g_s_tmsi(const uint64_t part1, const uint16_t part2);
|
||||
|
||||
/** @brief Deconstruct full 5G-S-TMSI into its components */
|
||||
void nr_deconstruct_5g_s_tmsi(const uint64_t fiveg_s_tmsi, uint16_t *amf_set_id, uint8_t *amf_pointer, uint32_t *m_tmsi);
|
||||
|
||||
#define CEILIDIV(a,b) ((a+b-1)/b)
|
||||
#define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1))
|
||||
#define BOUNDED_EVAL(a, b, c) (min(c, max(a, b)))
|
||||
|
||||
@@ -109,6 +109,15 @@ int hex_char_to_hex_value (char c);
|
||||
// Converts an hexadecimal ASCII coded string into its value.**
|
||||
int hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size);
|
||||
|
||||
/* Map task id to printable name. */
|
||||
typedef struct {
|
||||
int id;
|
||||
char text[256];
|
||||
} text_info_t;
|
||||
|
||||
#define TO_TEXT(LabEl, nUmID) {nUmID, #LabEl},
|
||||
#define TO_ENUM(LabEl, nUmID) LabEl = nUmID,
|
||||
|
||||
char *itoa(int i);
|
||||
|
||||
#define STRINGIFY(S) #S
|
||||
|
||||
Reference in New Issue
Block a user