一旦
This commit is contained in:
@@ -20,10 +20,10 @@
|
||||
*/
|
||||
|
||||
/*! \file PHY/NR_TRANSPORT/nr_antenna_control.c
|
||||
* \brief Test control interface implementation for antenna muting
|
||||
* \brief Antenna control interface implementation with O-RAN M-Plane support
|
||||
* \author OpenAirInterface
|
||||
* \date 2025
|
||||
* \version 1.0
|
||||
* \version 2.0
|
||||
*/
|
||||
|
||||
#include "nr_antenna_control.h"
|
||||
@@ -40,15 +40,31 @@
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
// Server state
|
||||
// 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)
|
||||
{
|
||||
@@ -113,7 +129,8 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
|
||||
" Bitmask: 0x%02X\n"
|
||||
"RX Array: %s\n"
|
||||
" Antennas: %d configured, %d active\n"
|
||||
" Bitmask: 0x%02X\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),
|
||||
@@ -121,7 +138,10 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
|
||||
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));
|
||||
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;
|
||||
@@ -137,9 +157,30 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
|
||||
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 Test Interface ===\n"
|
||||
"=== 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"
|
||||
@@ -150,10 +191,15 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
|
||||
" 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");
|
||||
"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");
|
||||
@@ -172,10 +218,11 @@ static void handle_client(int client_fd)
|
||||
char buffer[256];
|
||||
char response[1024];
|
||||
|
||||
// Send welcome message
|
||||
// Send welcome message with warning about non-compliance
|
||||
const char *welcome =
|
||||
"=== OAI Antenna Control Server ===\n"
|
||||
"O-RAN WG4 MPLANE-TRX-CONTROL Test Interface\n"
|
||||
"=== 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);
|
||||
|
||||
@@ -216,7 +263,7 @@ static void *server_thread(void *arg)
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t addr_len = sizeof(client_addr);
|
||||
|
||||
LOG_I(PHY, "[ANTENNA_CTRL] Server listening on port %d\n", g_ctrl.port);
|
||||
LOG_I(PHY, "[ANTENNA_CTRL] Test server listening on port %d\n", g_ctrl.port);
|
||||
|
||||
while (g_ctrl.running) {
|
||||
// Accept connection with timeout
|
||||
@@ -241,10 +288,50 @@ static void *server_thread(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
|
||||
// ============================================================================
|
||||
// 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] Server already running\n");
|
||||
LOG_W(PHY, "[ANTENNA_CTRL] Control interface already active\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,8 +340,15 @@ bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
|
||||
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_PORT;
|
||||
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);
|
||||
@@ -296,28 +390,43 @@ bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_I(PHY, "[ANTENNA_CTRL] Server started on port %d\n", g_ctrl.port);
|
||||
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;
|
||||
|
||||
// Close server socket to unblock accept()
|
||||
if (g_ctrl.server_fd >= 0) {
|
||||
close(g_ctrl.server_fd);
|
||||
g_ctrl.server_fd = -1;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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] Server stopped\n");
|
||||
LOG_I(PHY, "[ANTENNA_CTRL] Control interface stopped\n");
|
||||
}
|
||||
|
||||
bool nr_antenna_control_is_running(void)
|
||||
@@ -325,3 +434,98 @@ 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user