Files
openairinterface5g/radio/fhi_72/mplane/antenna-control.c
T
2026-01-30 23:38:37 +09:00

369 lines
12 KiB
C

#include "antenna-control.h"
#include "ru-mplane-api.h"
#include "xml/get-xml.h"
#include "common/utils/assertions.h"
// Forward declaration - actual function in config-mplane.c
extern bool edit_val_commmit_rpc(ru_session_t *ru_session, const char *content);
// Forward declaration - actual function in get-mplane.c
extern bool get_mplane(ru_session_t *ru_session, char **answer);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libyang/libyang.h>
#include <nc_client.h>
const char *antenna_ctrl_result_str(antenna_ctrl_result_t result)
{
switch (result) {
case ANTENNA_CTRL_SUCCESS:
return "Success";
case ANTENNA_CTRL_ERR_NO_SESSION:
return "No active NETCONF session";
case ANTENNA_CTRL_ERR_FEATURE_NOT_SUPPORTED:
return "MPLANE-TRX-CONTROL feature not supported by RU";
case ANTENNA_CTRL_ERR_INVALID_ARRAY_NAME:
return "Invalid array name";
case ANTENNA_CTRL_ERR_INVALID_MASK:
return "Invalid antenna bitmask";
case ANTENNA_CTRL_ERR_NETCONF_FAILED:
return "NETCONF RPC failed";
case ANTENNA_CTRL_ERR_VALIDATION_FAILED:
return "Configuration validation failed";
case ANTENNA_CTRL_ERR_COMMIT_FAILED:
return "Configuration commit failed";
default:
return "Unknown error";
}
}
/**
* @brief Convert antenna bitmask to O-RAN binary format
*
* O-RAN uses binary type in YANG which is base64 encoded in XML.
* For simplicity, we encode the bitmask as hex bytes.
*
* Per O-RAN spec: "The antenna mask contains as many binary values as
* number of array elements associated with respective tx or rx-array.
* Value of '1' indicates antenna element shall be kept active and
* value of '0' indicates antenna element should be turned inactive."
*/
static char *bitmask_to_base64(uint8_t bitmask, uint8_t num_antennas)
{
// For up to 8 antennas, we need 1 byte
// Base64 encoding of 1 byte = 4 characters + null terminator
char *result = malloc(8);
if (result == NULL) return NULL;
// Mask to only include configured antennas
uint8_t valid_mask = (1 << num_antennas) - 1;
uint8_t masked_value = bitmask & valid_mask;
// Simple base64 encoding for single byte
// Input: masked_value (8 bits) -> padded to 6+6+4 bits for base64
static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Encode single byte with padding
result[0] = b64chars[(masked_value >> 2) & 0x3F];
result[1] = b64chars[(masked_value << 4) & 0x30];
result[2] = '=';
result[3] = '=';
result[4] = '\0';
return result;
}
/**
* @brief Decode base64 to antenna bitmask
*/
static uint8_t base64_to_bitmask(const char *b64_str)
{
if (b64_str == NULL || strlen(b64_str) < 2) return 0xFF;
static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Find index of first two base64 characters
const char *p1 = strchr(b64chars, b64_str[0]);
const char *p2 = strchr(b64chars, b64_str[1]);
if (p1 == NULL || p2 == NULL) return 0xFF;
uint8_t val1 = (uint8_t)(p1 - b64chars);
uint8_t val2 = (uint8_t)(p2 - b64chars);
// Reconstruct byte
return (val1 << 2) | (val2 >> 4);
}
bool mplane_create_antenna_mask_xml(ru_session_t *ru_session,
bool is_tx,
const char *array_name,
uint8_t antenna_bitmask,
uint8_t num_antennas,
char **xml_content)
{
if (ru_session == NULL || ru_session->ctx == NULL || array_name == NULL || xml_content == NULL) {
return false;
}
*xml_content = NULL;
LY_ERR ret = LY_SUCCESS;
struct lyd_node *root = NULL;
// Create root node for o-ran-uplane-conf
ret = lyd_new_path(NULL, (struct ly_ctx *)ru_session->ctx,
"/o-ran-uplane-conf:user-plane-configuration", NULL, 0, &root);
if (ret != LY_SUCCESS) {
MP_LOG_W("Failed to create root user-plane-configuration node.\n");
return false;
}
// Create the antenna mask configuration
const char *list_name = is_tx ? "tx-array-antenna-mask-config" : "rx-array-antenna-mask-config";
struct lyd_node *mask_node = NULL;
ret = lyd_new_list(root, NULL, list_name, 0, &mask_node, array_name);
if (ret != LY_SUCCESS) {
MP_LOG_W("Failed to create %s list node for array '%s'.\n", list_name, array_name);
lyd_free_tree(root);
return false;
}
// Convert bitmask to base64 for YANG binary type
char *b64_mask = bitmask_to_base64(antenna_bitmask, num_antennas);
if (b64_mask == NULL) {
MP_LOG_W("Failed to encode antenna bitmask.\n");
lyd_free_tree(root);
return false;
}
// Add antenna-bitmask leaf
ret = lyd_new_term(mask_node, NULL, "antenna-bitmask", b64_mask, 0, NULL);
free(b64_mask);
if (ret != LY_SUCCESS) {
MP_LOG_W("Failed to create antenna-bitmask node.\n");
lyd_free_tree(root);
return false;
}
// Print to XML
// Note: LYD_PRINT_SIBLINGS in libyang v2.1+, LYD_PRINT_WITHSIBLINGS in older v2, LYP_WITHSIBLINGS in v1
#ifdef MPLANE_V1
lyd_print_mem(xml_content, root, LYD_XML, LYP_WITHSIBLINGS);
#elif defined MPLANE_V2
#ifdef LYD_PRINT_SIBLINGS
lyd_print_mem(xml_content, root, LYD_XML, LYD_PRINT_SIBLINGS);
#else
lyd_print_mem(xml_content, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
#endif
#endif
lyd_free_tree(root);
if (*xml_content == NULL) {
MP_LOG_W("Failed to generate XML content.\n");
return false;
}
MP_LOG_I("Generated %s antenna mask XML for array '%s', mask=0x%02X\n",
is_tx ? "TX" : "RX", array_name, antenna_bitmask);
return true;
}
antenna_ctrl_result_t mplane_set_tx_antenna_mask(ru_session_t *ru_session,
const char *array_name,
uint8_t antenna_bitmask,
uint8_t num_antennas)
{
if (ru_session == NULL || ru_session->session == NULL) {
return ANTENNA_CTRL_ERR_NO_SESSION;
}
if (array_name == NULL || strlen(array_name) == 0) {
return ANTENNA_CTRL_ERR_INVALID_ARRAY_NAME;
}
if (num_antennas == 0 || num_antennas > 8) {
return ANTENNA_CTRL_ERR_INVALID_MASK;
}
// Generate XML for antenna mask configuration
char *xml_content = NULL;
if (!mplane_create_antenna_mask_xml(ru_session, true, array_name,
antenna_bitmask, num_antennas, &xml_content)) {
return ANTENNA_CTRL_ERR_NETCONF_FAILED;
}
MP_LOG_I("Setting TX antenna mask via NETCONF:\n%s\n", xml_content);
// Use existing M-Plane infrastructure to send edit-config, validate, commit
bool success = edit_val_commmit_rpc(ru_session, xml_content);
free(xml_content);
if (!success) {
MP_LOG_W("Failed to configure TX antenna mask for array '%s'\n", array_name);
return ANTENNA_CTRL_ERR_COMMIT_FAILED;
}
MP_LOG_I("Successfully configured TX antenna mask 0x%02X for array '%s'\n",
antenna_bitmask, array_name);
return ANTENNA_CTRL_SUCCESS;
}
antenna_ctrl_result_t mplane_set_rx_antenna_mask(ru_session_t *ru_session,
const char *array_name,
uint8_t antenna_bitmask,
uint8_t num_antennas)
{
if (ru_session == NULL || ru_session->session == NULL) {
return ANTENNA_CTRL_ERR_NO_SESSION;
}
if (array_name == NULL || strlen(array_name) == 0) {
return ANTENNA_CTRL_ERR_INVALID_ARRAY_NAME;
}
if (num_antennas == 0 || num_antennas > 8) {
return ANTENNA_CTRL_ERR_INVALID_MASK;
}
// Generate XML for antenna mask configuration
char *xml_content = NULL;
if (!mplane_create_antenna_mask_xml(ru_session, false, array_name,
antenna_bitmask, num_antennas, &xml_content)) {
return ANTENNA_CTRL_ERR_NETCONF_FAILED;
}
MP_LOG_I("Setting RX antenna mask via NETCONF:\n%s\n", xml_content);
// Use existing M-Plane infrastructure to send edit-config, validate, commit
bool success = edit_val_commmit_rpc(ru_session, xml_content);
free(xml_content);
if (!success) {
MP_LOG_W("Failed to configure RX antenna mask for array '%s'\n", array_name);
return ANTENNA_CTRL_ERR_COMMIT_FAILED;
}
MP_LOG_I("Successfully configured RX antenna mask 0x%02X for array '%s'\n",
antenna_bitmask, array_name);
return ANTENNA_CTRL_SUCCESS;
}
antenna_ctrl_result_t mplane_get_tx_antenna_mask(ru_session_t *ru_session,
const char *array_name,
uint8_t *antenna_bitmask)
{
if (ru_session == NULL || ru_session->session == NULL) {
return ANTENNA_CTRL_ERR_NO_SESSION;
}
if (array_name == NULL || antenna_bitmask == NULL) {
return ANTENNA_CTRL_ERR_INVALID_ARRAY_NAME;
}
// Get operational datastore
char *operational_ds = NULL;
if (!get_mplane(ru_session, &operational_ds)) {
return ANTENNA_CTRL_ERR_NETCONF_FAILED;
}
// Build XPath to find the antenna mask for this array
char xpath[256];
snprintf(xpath, sizeof(xpath),
"tx-array-antenna-mask-config[array-name='%s']/antenna-bitmask",
array_name);
// Extract the value from XML response
char *mask_b64 = get_ru_xml_node(operational_ds, "antenna-bitmask");
free(operational_ds);
if (mask_b64 == NULL) {
// No mask configured, assume all active
*antenna_bitmask = 0xFF;
return ANTENNA_CTRL_SUCCESS;
}
*antenna_bitmask = base64_to_bitmask(mask_b64);
free(mask_b64);
MP_LOG_I("Retrieved TX antenna mask 0x%02X for array '%s'\n",
*antenna_bitmask, array_name);
return ANTENNA_CTRL_SUCCESS;
}
antenna_ctrl_result_t mplane_get_rx_antenna_mask(ru_session_t *ru_session,
const char *array_name,
uint8_t *antenna_bitmask)
{
if (ru_session == NULL || ru_session->session == NULL) {
return ANTENNA_CTRL_ERR_NO_SESSION;
}
if (array_name == NULL || antenna_bitmask == NULL) {
return ANTENNA_CTRL_ERR_INVALID_ARRAY_NAME;
}
// Get operational datastore
char *operational_ds = NULL;
if (!get_mplane(ru_session, &operational_ds)) {
return ANTENNA_CTRL_ERR_NETCONF_FAILED;
}
// Extract the value from XML response (simplified - real impl would use XPath)
char *mask_b64 = get_ru_xml_node(operational_ds, "antenna-bitmask");
free(operational_ds);
if (mask_b64 == NULL) {
// No mask configured, assume all active
*antenna_bitmask = 0xFF;
return ANTENNA_CTRL_SUCCESS;
}
*antenna_bitmask = base64_to_bitmask(mask_b64);
free(mask_b64);
MP_LOG_I("Retrieved RX antenna mask 0x%02X for array '%s'\n",
*antenna_bitmask, array_name);
return ANTENNA_CTRL_SUCCESS;
}
bool mplane_supports_trx_control(ru_session_t *ru_session)
{
if (ru_session == NULL || ru_session->session == NULL) {
return false;
}
// Check if MPLANE-TRX-CONTROL feature is advertised in RU capabilities
// This is done by checking the server capabilities from the hello exchange
// or by querying the YANG library
struct nc_session *session = (struct nc_session *)ru_session->session;
const char * const *caps = nc_session_get_cpblts(session);
if (caps == NULL) {
return false;
}
// Look for o-ran-uplane-conf module with MPLANE-TRX-CONTROL feature
for (int i = 0; caps[i] != NULL; i++) {
if (strstr(caps[i], "o-ran-uplane-conf") != NULL) {
// Module is supported, assume feature is enabled
// A more complete implementation would query module features
MP_LOG_I("RU supports o-ran-uplane-conf module (MPLANE-TRX-CONTROL)\n");
return true;
}
}
MP_LOG_W("RU does not advertise o-ran-uplane-conf module\n");
return false;
}