532 lines
18 KiB
C
532 lines
18 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
/*! \file PHY/NR_TRANSPORT/nr_antenna_control.c
|
|
* \brief Antenna control interface implementation with O-RAN M-Plane support
|
|
* \author OpenAirInterface
|
|
* \date 2025
|
|
* \version 2.0
|
|
*/
|
|
|
|
#include "nr_antenna_control.h"
|
|
#include "common/utils/LOG/log.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
// Try to include M-Plane header if available
|
|
#ifdef OAI_MPLANE
|
|
#include "radio/fhi_72/mplane/antenna-control-mplane.h"
|
|
#include "radio/fhi_72/mplane/ru-mplane-api.h"
|
|
#endif
|
|
|
|
// Global control state
|
|
static struct {
|
|
pthread_t thread;
|
|
int server_fd;
|
|
nr_antenna_state_t *ant_state;
|
|
volatile bool running;
|
|
int port;
|
|
nr_antenna_ctrl_backend_t backend;
|
|
#ifdef OAI_MPLANE
|
|
ru_session_t *ru_session;
|
|
#else
|
|
void *ru_session;
|
|
#endif
|
|
} g_ctrl = {0};
|
|
|
|
// ============================================================================
|
|
// Test Socket Server Implementation (Development/Testing Mode)
|
|
// ============================================================================
|
|
|
|
// Process a single command and generate response
|
|
static void process_command(const char *cmd, char *response, size_t resp_size)
|
|
{
|
|
char cmd_upper[256];
|
|
strncpy(cmd_upper, cmd, sizeof(cmd_upper) - 1);
|
|
cmd_upper[sizeof(cmd_upper) - 1] = '\0';
|
|
|
|
// Convert to uppercase for case-insensitive matching
|
|
for (char *p = cmd_upper; *p; p++) {
|
|
if (*p >= 'a' && *p <= 'z') *p -= 32;
|
|
}
|
|
|
|
// Trim whitespace
|
|
char *p = cmd_upper;
|
|
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
|
char *end = p + strlen(p) - 1;
|
|
while (end > p && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')) *end-- = '\0';
|
|
|
|
if (strncmp(p, "MUTE TX ", 8) == 0) {
|
|
int ant_id = atoi(p + 8);
|
|
nr_mute_tx_antenna(g_ctrl.ant_state, ant_id);
|
|
snprintf(response, resp_size, "OK: TX antenna %d muted\n", ant_id);
|
|
}
|
|
else if (strncmp(p, "UNMUTE TX ", 10) == 0) {
|
|
int ant_id = atoi(p + 10);
|
|
nr_unmute_tx_antenna(g_ctrl.ant_state, ant_id);
|
|
snprintf(response, resp_size, "OK: TX antenna %d unmuted\n", ant_id);
|
|
}
|
|
else if (strncmp(p, "MUTE RX ", 8) == 0) {
|
|
int ant_id = atoi(p + 8);
|
|
nr_mute_rx_antenna(g_ctrl.ant_state, ant_id);
|
|
snprintf(response, resp_size, "OK: RX antenna %d muted\n", ant_id);
|
|
}
|
|
else if (strncmp(p, "UNMUTE RX ", 10) == 0) {
|
|
int ant_id = atoi(p + 10);
|
|
nr_unmute_rx_antenna(g_ctrl.ant_state, ant_id);
|
|
snprintf(response, resp_size, "OK: RX antenna %d unmuted\n", ant_id);
|
|
}
|
|
else if (strncmp(p, "SET TX MASK ", 12) == 0) {
|
|
unsigned int mask;
|
|
if (sscanf(p + 12, "%x", &mask) == 1 || sscanf(p + 12, "0X%x", &mask) == 1) {
|
|
uint8_t old = nr_set_tx_antenna_bitmask(g_ctrl.ant_state, (uint8_t)mask);
|
|
snprintf(response, resp_size, "OK: TX mask 0x%02X -> 0x%02X\n", old, (uint8_t)mask);
|
|
} else {
|
|
snprintf(response, resp_size, "ERROR: Invalid mask format. Use hex (e.g., 0F or 0x0F)\n");
|
|
}
|
|
}
|
|
else if (strncmp(p, "SET RX MASK ", 12) == 0) {
|
|
unsigned int mask;
|
|
if (sscanf(p + 12, "%x", &mask) == 1 || sscanf(p + 12, "0X%x", &mask) == 1) {
|
|
uint8_t old = nr_set_rx_antenna_bitmask(g_ctrl.ant_state, (uint8_t)mask);
|
|
snprintf(response, resp_size, "OK: RX mask 0x%02X -> 0x%02X\n", old, (uint8_t)mask);
|
|
} else {
|
|
snprintf(response, resp_size, "ERROR: Invalid mask format. Use hex (e.g., 0F or 0x0F)\n");
|
|
}
|
|
}
|
|
else if (strcmp(p, "STATUS") == 0) {
|
|
snprintf(response, resp_size,
|
|
"=== Antenna Status ===\n"
|
|
"TX Array: %s\n"
|
|
" Antennas: %d configured, %d active\n"
|
|
" Bitmask: 0x%02X\n"
|
|
"RX Array: %s\n"
|
|
" Antennas: %d configured, %d active\n"
|
|
" Bitmask: 0x%02X\n"
|
|
"Backend: %s\n",
|
|
g_ctrl.ant_state->tx_array_name,
|
|
g_ctrl.ant_state->num_tx_antennas,
|
|
nr_get_active_tx_antenna_count(g_ctrl.ant_state),
|
|
nr_get_tx_antenna_bitmask(g_ctrl.ant_state),
|
|
g_ctrl.ant_state->rx_array_name,
|
|
g_ctrl.ant_state->num_rx_antennas,
|
|
nr_get_active_rx_antenna_count(g_ctrl.ant_state),
|
|
nr_get_rx_antenna_bitmask(g_ctrl.ant_state),
|
|
g_ctrl.backend == ANTENNA_CTRL_BACKEND_MPLANE ? "O-RAN M-Plane NETCONF" :
|
|
g_ctrl.backend == ANTENNA_CTRL_BACKEND_TEST_SOCKET ? "Test Socket (NOT O-RAN)" :
|
|
"None");
|
|
}
|
|
else if (strcmp(p, "STATS") == 0) {
|
|
uint64_t tx_events, rx_events, muted_slots;
|
|
nr_get_antenna_stats(g_ctrl.ant_state, &tx_events, &rx_events, &muted_slots);
|
|
snprintf(response, resp_size,
|
|
"=== Energy Saving Statistics ===\n"
|
|
"TX mute events: %lu\n"
|
|
"RX mute events: %lu\n"
|
|
"Total muted slots: %lu\n",
|
|
tx_events, rx_events, muted_slots);
|
|
}
|
|
else if (strcmp(p, "RESET STATS") == 0) {
|
|
nr_reset_antenna_stats(g_ctrl.ant_state);
|
|
snprintf(response, resp_size, "OK: Statistics reset\n");
|
|
}
|
|
else if (strcmp(p, "RESET") == 0 || strcmp(p, "UNMUTE ALL") == 0) {
|
|
// Reset all antennas to active state
|
|
uint8_t all_tx = (1 << g_ctrl.ant_state->num_tx_antennas) - 1;
|
|
uint8_t all_rx = (1 << g_ctrl.ant_state->num_rx_antennas) - 1;
|
|
nr_set_tx_antenna_bitmask(g_ctrl.ant_state, all_tx);
|
|
nr_set_rx_antenna_bitmask(g_ctrl.ant_state, all_rx);
|
|
snprintf(response, resp_size,
|
|
"OK: All antennas unmuted\n"
|
|
" TX mask: 0x%02X (%d antennas)\n"
|
|
" RX mask: 0x%02X (%d antennas)\n",
|
|
all_tx, g_ctrl.ant_state->num_tx_antennas,
|
|
all_rx, g_ctrl.ant_state->num_rx_antennas);
|
|
}
|
|
else if (strcmp(p, "SYNC") == 0) {
|
|
// Synchronize to RU (only works with M-Plane backend)
|
|
if (nr_antenna_control_sync_to_ru(g_ctrl.ant_state)) {
|
|
snprintf(response, resp_size, "OK: Synchronized to RU\n");
|
|
} else {
|
|
snprintf(response, resp_size, "ERROR: Sync failed (M-Plane backend required)\n");
|
|
}
|
|
}
|
|
else if (strcmp(p, "HELP") == 0) {
|
|
snprintf(response, resp_size,
|
|
"=== O-RAN Antenna Control Interface ===\n"
|
|
"Commands:\n"
|
|
" MUTE TX <ant_id> - Mute TX antenna (0-based)\n"
|
|
" UNMUTE TX <ant_id> - Unmute TX antenna\n"
|
|
" MUTE RX <ant_id> - Mute RX antenna\n"
|
|
" UNMUTE RX <ant_id> - Unmute RX antenna\n"
|
|
" SET TX MASK <hex> - Set TX bitmask (O-RAN compliant)\n"
|
|
" SET RX MASK <hex> - Set RX bitmask (O-RAN compliant)\n"
|
|
" STATUS - Show current antenna state\n"
|
|
" STATS - Show energy saving statistics\n"
|
|
" RESET STATS - Reset statistics\n"
|
|
" RESET / UNMUTE ALL - Unmute all antennas (restore default)\n"
|
|
" SYNC - Sync state to RU (M-Plane only)\n"
|
|
" HELP - Show this help\n"
|
|
" QUIT - Close connection\n"
|
|
"\n"
|
|
"Example: SET TX MASK 03 (enables antennas 0,1; mutes 2,3)\n"
|
|
"\n"
|
|
"NOTE: This test socket interface is NOT O-RAN compliant.\n"
|
|
" For production, use NETCONF via radio/fhi_72/mplane/\n");
|
|
}
|
|
else if (strcmp(p, "QUIT") == 0 || strcmp(p, "EXIT") == 0) {
|
|
snprintf(response, resp_size, "BYE\n");
|
|
}
|
|
else if (strlen(p) == 0) {
|
|
response[0] = '\0'; // Empty command, no response
|
|
}
|
|
else {
|
|
snprintf(response, resp_size, "ERROR: Unknown command '%s'. Type HELP for usage.\n", cmd);
|
|
}
|
|
}
|
|
|
|
// Handle a single client connection
|
|
static void handle_client(int client_fd)
|
|
{
|
|
char buffer[256];
|
|
char response[1024];
|
|
|
|
// Send welcome message with warning about non-compliance
|
|
const char *welcome =
|
|
"=== OAI Antenna Control Test Server ===\n"
|
|
"WARNING: This is a TEST interface, NOT O-RAN compliant!\n"
|
|
"For production, use NETCONF via radio/fhi_72/mplane/\n"
|
|
"Type HELP for commands.\n\n";
|
|
send(client_fd, welcome, strlen(welcome), 0);
|
|
|
|
while (g_ctrl.running) {
|
|
// Send prompt
|
|
send(client_fd, "> ", 2, 0);
|
|
|
|
// Receive command
|
|
ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
|
if (n <= 0) break;
|
|
|
|
buffer[n] = '\0';
|
|
|
|
// Process command
|
|
process_command(buffer, response, sizeof(response));
|
|
|
|
// Send response
|
|
if (strlen(response) > 0) {
|
|
send(client_fd, response, strlen(response), 0);
|
|
}
|
|
|
|
// Check for quit
|
|
if (strstr(buffer, "QUIT") || strstr(buffer, "quit") ||
|
|
strstr(buffer, "EXIT") || strstr(buffer, "exit")) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
close(client_fd);
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Client disconnected\n");
|
|
}
|
|
|
|
// Server thread function
|
|
static void *server_thread(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
struct sockaddr_in client_addr;
|
|
socklen_t addr_len = sizeof(client_addr);
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Test server listening on port %d\n", g_ctrl.port);
|
|
|
|
while (g_ctrl.running) {
|
|
// Accept connection with timeout
|
|
fd_set read_fds;
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(g_ctrl.server_fd, &read_fds);
|
|
|
|
struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
|
|
int ret = select(g_ctrl.server_fd + 1, &read_fds, NULL, NULL, &tv);
|
|
|
|
if (ret <= 0) continue;
|
|
|
|
int client_fd = accept(g_ctrl.server_fd, (struct sockaddr *)&client_addr, &addr_len);
|
|
if (client_fd < 0) continue;
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Client connected from %s:%d\n",
|
|
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
|
|
|
handle_client(client_fd);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Public API Implementation
|
|
// ============================================================================
|
|
|
|
bool nr_antenna_control_init_mplane(nr_antenna_state_t *ant_state, void *ru_session)
|
|
{
|
|
#ifdef OAI_MPLANE
|
|
if (g_ctrl.running) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Control interface already active\n");
|
|
return false;
|
|
}
|
|
|
|
if (ant_state == NULL || ru_session == NULL) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Invalid parameters\n");
|
|
return false;
|
|
}
|
|
|
|
g_ctrl.ant_state = ant_state;
|
|
g_ctrl.ru_session = (ru_session_t *)ru_session;
|
|
g_ctrl.backend = ANTENNA_CTRL_BACKEND_MPLANE;
|
|
g_ctrl.running = true;
|
|
|
|
// Check if RU supports TRX control feature
|
|
if (!mplane_supports_trx_control(g_ctrl.ru_session)) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] RU does not support MPLANE-TRX-CONTROL feature\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Antenna muting may not work on this RU\n");
|
|
}
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Initialized with O-RAN M-Plane NETCONF backend\n");
|
|
LOG_I(PHY, "[ANTENNA_CTRL] TX array: %s, RX array: %s\n",
|
|
ant_state->tx_array_name, ant_state->rx_array_name);
|
|
|
|
return true;
|
|
#else
|
|
LOG_E(PHY, "[ANTENNA_CTRL] M-Plane support not compiled in (OAI_MPLANE not defined)\n");
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Use nr_antenna_control_start_test_server() for testing\n");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool nr_antenna_control_start_test_server(nr_antenna_state_t *ant_state, int port)
|
|
{
|
|
if (g_ctrl.running) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Control interface already active\n");
|
|
return false;
|
|
}
|
|
|
|
if (ant_state == NULL) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Antenna state is NULL\n");
|
|
return false;
|
|
}
|
|
|
|
LOG_W(PHY, "[ANTENNA_CTRL] ======================================================\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Starting TEST socket server - NOT O-RAN COMPLIANT!\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] For production O-RAN compliance, use M-Plane NETCONF\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] See: radio/fhi_72/mplane/antenna-control-mplane.h\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] ======================================================\n");
|
|
|
|
g_ctrl.ant_state = ant_state;
|
|
g_ctrl.port = (port > 0) ? port : NR_ANTENNA_CONTROL_TEST_PORT;
|
|
g_ctrl.backend = ANTENNA_CTRL_BACKEND_TEST_SOCKET;
|
|
|
|
// Create socket
|
|
g_ctrl.server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (g_ctrl.server_fd < 0) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Failed to create socket: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
// Allow address reuse
|
|
int opt = 1;
|
|
setsockopt(g_ctrl.server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
|
// Bind
|
|
struct sockaddr_in addr = {
|
|
.sin_family = AF_INET,
|
|
.sin_addr.s_addr = INADDR_ANY,
|
|
.sin_port = htons(g_ctrl.port)
|
|
};
|
|
|
|
if (bind(g_ctrl.server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Failed to bind port %d: %s\n", g_ctrl.port, strerror(errno));
|
|
close(g_ctrl.server_fd);
|
|
return false;
|
|
}
|
|
|
|
// Listen
|
|
if (listen(g_ctrl.server_fd, 1) < 0) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Failed to listen: %s\n", strerror(errno));
|
|
close(g_ctrl.server_fd);
|
|
return false;
|
|
}
|
|
|
|
// Start server thread
|
|
g_ctrl.running = true;
|
|
if (pthread_create(&g_ctrl.thread, NULL, server_thread, NULL) != 0) {
|
|
LOG_E(PHY, "[ANTENNA_CTRL] Failed to create server thread\n");
|
|
g_ctrl.running = false;
|
|
close(g_ctrl.server_fd);
|
|
return false;
|
|
}
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Test server started on port %d\n", g_ctrl.port);
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Connect with: nc localhost %d\n", g_ctrl.port);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
|
|
{
|
|
LOG_W(PHY, "[ANTENNA_CTRL] nr_antenna_control_start() is DEPRECATED\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Use nr_antenna_control_init_mplane() for O-RAN compliance\n");
|
|
LOG_W(PHY, "[ANTENNA_CTRL] or nr_antenna_control_start_test_server() for testing\n");
|
|
|
|
return nr_antenna_control_start_test_server(ant_state, port);
|
|
}
|
|
|
|
void nr_antenna_control_stop(void)
|
|
{
|
|
if (!g_ctrl.running) return;
|
|
|
|
g_ctrl.running = false;
|
|
|
|
if (g_ctrl.backend == ANTENNA_CTRL_BACKEND_TEST_SOCKET) {
|
|
// Close server socket to unblock accept()
|
|
if (g_ctrl.server_fd >= 0) {
|
|
close(g_ctrl.server_fd);
|
|
g_ctrl.server_fd = -1;
|
|
}
|
|
|
|
// Wait for thread
|
|
pthread_join(g_ctrl.thread, NULL);
|
|
}
|
|
|
|
g_ctrl.backend = ANTENNA_CTRL_BACKEND_NONE;
|
|
g_ctrl.ant_state = NULL;
|
|
g_ctrl.ru_session = NULL;
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Control interface stopped\n");
|
|
}
|
|
|
|
bool nr_antenna_control_is_running(void)
|
|
{
|
|
return g_ctrl.running;
|
|
}
|
|
|
|
nr_antenna_ctrl_backend_t nr_antenna_control_get_backend(void)
|
|
{
|
|
return g_ctrl.backend;
|
|
}
|
|
|
|
bool nr_antenna_control_sync_to_ru(nr_antenna_state_t *ant_state)
|
|
{
|
|
#ifdef OAI_MPLANE
|
|
if (g_ctrl.backend != ANTENNA_CTRL_BACKEND_MPLANE || g_ctrl.ru_session == NULL) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane backend not active, cannot sync to RU\n");
|
|
return false;
|
|
}
|
|
|
|
if (ant_state == NULL) {
|
|
ant_state = g_ctrl.ant_state;
|
|
}
|
|
|
|
if (ant_state == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// Send TX antenna mask to RU
|
|
antenna_ctrl_result_t result;
|
|
result = mplane_set_tx_antenna_mask(g_ctrl.ru_session,
|
|
ant_state->tx_array_name,
|
|
nr_get_tx_antenna_bitmask(ant_state),
|
|
ant_state->num_tx_antennas);
|
|
if (result != ANTENNA_CTRL_SUCCESS) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Failed to sync TX mask: %s\n",
|
|
antenna_ctrl_result_str(result));
|
|
return false;
|
|
}
|
|
|
|
// Send RX antenna mask to RU
|
|
result = mplane_set_rx_antenna_mask(g_ctrl.ru_session,
|
|
ant_state->rx_array_name,
|
|
nr_get_rx_antenna_bitmask(ant_state),
|
|
ant_state->num_rx_antennas);
|
|
if (result != ANTENNA_CTRL_SUCCESS) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] Failed to sync RX mask: %s\n",
|
|
antenna_ctrl_result_str(result));
|
|
return false;
|
|
}
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Successfully synchronized antenna masks to RU\n");
|
|
return true;
|
|
#else
|
|
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane not compiled in\n");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool nr_antenna_control_sync_from_ru(nr_antenna_state_t *ant_state)
|
|
{
|
|
#ifdef OAI_MPLANE
|
|
if (g_ctrl.backend != ANTENNA_CTRL_BACKEND_MPLANE || g_ctrl.ru_session == NULL) {
|
|
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane backend not active, cannot sync from RU\n");
|
|
return false;
|
|
}
|
|
|
|
if (ant_state == NULL) {
|
|
ant_state = g_ctrl.ant_state;
|
|
}
|
|
|
|
if (ant_state == NULL) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t tx_mask, rx_mask;
|
|
antenna_ctrl_result_t result;
|
|
|
|
// Get TX antenna mask from RU
|
|
result = mplane_get_tx_antenna_mask(g_ctrl.ru_session,
|
|
ant_state->tx_array_name,
|
|
&tx_mask);
|
|
if (result == ANTENNA_CTRL_SUCCESS) {
|
|
nr_set_tx_antenna_bitmask(ant_state, tx_mask);
|
|
}
|
|
|
|
// Get RX antenna mask from RU
|
|
result = mplane_get_rx_antenna_mask(g_ctrl.ru_session,
|
|
ant_state->rx_array_name,
|
|
&rx_mask);
|
|
if (result == ANTENNA_CTRL_SUCCESS) {
|
|
nr_set_rx_antenna_bitmask(ant_state, rx_mask);
|
|
}
|
|
|
|
LOG_I(PHY, "[ANTENNA_CTRL] Synchronized antenna masks from RU: TX=0x%02X, RX=0x%02X\n",
|
|
tx_mask, rx_mask);
|
|
return true;
|
|
#else
|
|
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane not compiled in\n");
|
|
return false;
|
|
#endif
|
|
}
|