/* * SPDX-License-Identifier: LicenseRef-CSSL-1.0 */ /*! * \brief OAI MAC/Aerial L1 interface file using FAPI over shared memory */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fapi_nvIPC.h" #include #include #include #include "nfapi_interface.h" #include "nfapi.h" #include "nfapi_nr_interface_scf.h" #include "nfapi/open-nFAPI/vnf/inc/vnf_p7.h" #include "system.h" #include "fapi_vnf_p7.h" #define MAX_EVENTS 10 #define RECV_BUF_LEN 8192 char cpu_buf_recv[RECV_BUF_LEN]; uint16_t sfn = 0, slot = 0; nv_ipc_t *ipc; static nv_ipc_config_t nv_ipc_config; static int cpu_msg_buf_size = 0; static int cpu_data_buf_size = 0; static int cpu_large_buf_size = 0; void nvIPC_Stop() { LOG_I(NR_MAC, "Received STOP.indication\n"); ((vnf_t *)get_config())->terminate = true; } void nvIPC_send_stop_request() { nfapi_nr_stop_request_scf_t req = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST, .header.phy_id = 0}; LOG_I(NR_MAC, "Sending NFAPI STOP.request\n"); nfapi_nr_vnf_stop_req(get_config(), get_config()->pnf_list->p5_idx, &req); } static uint16_t old_sfn = 0; static uint16_t old_slot = 0; //////////////////////////////////////////////////////////////////////// // Handle an RX message static int ipc_handle_rx_msg(nv_ipc_msg_t *msg) { if (msg == NULL) { LOG_E(NFAPI_VNF, "%s: ERROR: buffer is empty\n", __func__); return -1; } uint8_t *pReadPackedMessage = msg->msg_buf; uint8_t *end = msg->msg_buf + msg->msg_len; // unpack FAPI messages and handle them nfapi_vnf_config_t * vnf_config = get_config(); if (vnf_config != 0) { // first, unpack the header fapi_message_header_t fapi_msg; if (!(pull8(&pReadPackedMessage, &fapi_msg.num_msg, end) && pull8(&pReadPackedMessage, &fapi_msg.opaque_handle, end) && pull16(&pReadPackedMessage, &fapi_msg.message_id, end) && pull32(&pReadPackedMessage, &fapi_msg.message_length, end))) { NFAPI_TRACE(NFAPI_TRACE_ERROR, "FAPI message header unpack failed\n"); return -1; } vnf_p7_t *vnf_p7_config = (vnf_p7_t *)((vnf_info *)vnf_config->user_data)->p7_vnfs->config; switch (fapi_msg.message_id) { case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE ... NFAPI_NR_PHY_MSG_TYPE_ERROR_INDICATION: vnf_nr_handle_p4_p5_message(msg->msg_buf, msg->msg_len, 1, vnf_config); break; // P7 Messages case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: { uint8_t *pReadData = msg->data_buf; int dataBufLen = msg->data_len; uint8_t *data_end = msg->data_buf + dataBufLen; nfapi_nr_rx_data_indication_t ind; ind.header.message_id = fapi_msg.message_id; ind.header.message_length = fapi_msg.message_length; aerial_unpack_nr_rx_data_indication( &pReadPackedMessage, end, &pReadData, data_end, &ind, &vnf_p7_config->_public.codec_config); NFAPI_TRACE(NFAPI_TRACE_INFO, "%s: Handling RX Indication\n", __FUNCTION__); if (vnf_p7_config->_public.nr_rx_data_indication) { (vnf_p7_config->_public.nr_rx_data_indication)(&ind); for (int i = 0; i < ind.number_of_pdus; ++i) { free(ind.pdu_list[i].pdu); } free(ind.pdu_list); } break; } case NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION: { uint8_t *pReadData = msg->data_buf; int dataBufLen = msg->data_len; uint8_t *data_end = msg->data_buf + dataBufLen; nfapi_nr_srs_indication_t ind; aerial_unpack_nr_srs_indication(&pReadPackedMessage, end, &pReadData, data_end, &ind); if (vnf_p7_config->_public.nr_srs_indication) { (vnf_p7_config->_public.nr_srs_indication)(&ind); } break; } case NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION: { nfapi_nr_slot_indication_scf_t ind; if (!vnf_p7_config->_public.unpack_func(msg->msg_buf, msg->msg_len, &ind, sizeof(ind), &vnf_p7_config->_public.codec_config)) { NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); } else { NFAPI_TRACE(NFAPI_TRACE_DEBUG, "%s: Handling NR SLOT Indication\n", __FUNCTION__); // check if the sfn/slot unpacked come wrong at any time, should be old + 1 (slot 0 -- 19, sfn 0 -- 1023) // add 1 to current sfn number uint16_t old_slot_plus = ((old_slot + 1) % 20); uint16_t old_sfn_plus = old_slot_plus == 0 ? ((old_sfn + 1) % 1024) : old_sfn; if (old_slot_plus != ind.slot || old_sfn_plus != ind.sfn) { LOG_E(NFAPI_VNF, "\n============================================================================\n" "sfn slot doesn't match unpacked one! L2->L1 %d.%d vs L1->L2 %d.%d \n" "============================================================================\n", old_sfn, old_slot, ind.sfn, ind.slot); } old_sfn = ind.sfn; old_slot = ind.slot; if (vnf_p7_config->_public.nr_slot_indication) { (vnf_p7_config->_public.nr_slot_indication)(&ind); } } break; } case NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION: case NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION: case NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: { vnf_nr_handle_p7_message(msg->msg_buf, msg->msg_len, vnf_p7_config); break; } default: { NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s P5 Unknown message ID %d\n", __FUNCTION__, fapi_msg.message_id); break; } } } return 0; } int get_cpu_msg_buf_size() { return cpu_msg_buf_size; } int get_cpu_data_buf_size() { return cpu_data_buf_size; } bool allocate_msg(nv_ipc_msg_t *send_msg) { return ipc->tx_allocate(ipc, send_msg, 0) == 0; } void release_msg(nv_ipc_msg_t *send_msg) { ipc->tx_release(ipc, send_msg); } bool send_nvipc_msg(nv_ipc_msg_t *send_msg) { int send_retval = ipc->tx_send_msg(ipc, send_msg); if (send_retval != 0) { LOG_E(NFAPI_VNF, "%s error: send TX message failed Error: %d\n", __FUNCTION__, send_retval); ipc->tx_release(ipc, send_msg); return false; } ipc->notify(ipc, 1); // notify that there's 1 message in queue return true; } bool aerial_nr_send_p5_message(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) { nfapi_vnf_pnf_info_t *pnf = nfapi_vnf_pnf_list_find(&(vnf->_public), p5_idx); if (pnf) { // Create the message nv_ipc_msg_t send_msg = {.msg_id = msg->message_id, .cell_id = 0, // By default, P5 uses only message pool. .data_pool = NV_IPC_MEMPOOL_CPU_MSG, .data_len = 0, .data_buf = NULL}; bool has_separate_dbt_payload = false; nfapi_nr_config_request_scf_t *config_req = NULL; if (msg->message_id == NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST) { config_req = (nfapi_nr_config_request_scf_t *)msg; has_separate_dbt_payload = config_req->dbt_config.num_dig_beams > 0; if (has_separate_dbt_payload) { send_msg.data_pool = NV_IPC_MEMPOOL_CPU_LARGE; } } // Allocate the message if (!allocate_msg(&send_msg)) { return false; } int packedMessageLengthFAPI = -1; // Pack directly into it if ((packedMessageLengthFAPI = vnf->_public.pack_func(msg, msg_len, send_msg.msg_buf, get_cpu_msg_buf_size(), &vnf->_public.codec_config)) < 0) { release_msg(&send_msg); return false; } if (packedMessageLengthFAPI <= 0) { LOG_E(NFAPI_VNF, "Error packing message 0x%02x\n", msg->message_id); release_msg(&send_msg); return false; } // Set the length send_msg.msg_len = packedMessageLengthFAPI + 8; // adding 8 to account for the size of the FAPI header if (has_separate_dbt_payload) { AssertFatal(send_msg.data_buf != NULL, "CONFIG.request DBT path: data buffer is NULL\n"); AssertFatal(cpu_large_buf_size > 0, "CONFIG.request DBT path: CPU_LARGE buffer size is not set\n"); uint8_t *write_ptr = send_msg.data_buf; uint8_t *buffer_end = send_msg.data_buf + cpu_large_buf_size; nfapi_nr_dbt_tlv_ve_t dbt_tlv = { .tl.tag = NFAPI_NR_CONFIG_BEAMFORMING_TABLE_TAG, .value = config_req->dbt_config }; bool ok = pack_dbt_table_tlv_value(&dbt_tlv, &write_ptr, buffer_end); if (!ok) { LOG_E(NFAPI_VNF, "Failed to pack CONFIG.request DBT payload into separate buffer\n"); release_msg(&send_msg); return false; } send_msg.data_len = (uint32_t)(write_ptr - (uint8_t *)send_msg.data_buf); LOG_I(NFAPI_VNF, "CONFIG.request DBT sent in separate pool: num_beams=%u num_txrus=%u data_len=%d pool=%d\n", config_req->dbt_config.num_dig_beams, config_req->dbt_config.num_txrus, send_msg.data_len, send_msg.data_pool); } // Send return send_nvipc_msg(&send_msg); } else { NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() cannot find pnf info for p5_idx:%d\n", __FUNCTION__, p5_idx); return false; } } // Always allocate message buffer, but allocate data buffer only when data_len > 0 static int aerial_recv_msg(nv_ipc_msg_t *recv_msg) { if (ipc == NULL) { return -1; } recv_msg->msg_buf = NULL; recv_msg->data_buf = NULL; // Allocate buffer for TX message if (ipc->rx_recv_msg(ipc, recv_msg) < 0) { LOG_D(NFAPI_VNF, "%s: no more message available\n", __func__); return -1; } LOG_D(NFAPI_VNF, "recv: cell_id=%d msg_id=0x%02X msg_len=%d data_len=%d data_pool=%d\n", recv_msg->cell_id, recv_msg->msg_id, recv_msg->msg_len, recv_msg->data_len, recv_msg->data_pool); ipc_handle_rx_msg(recv_msg); // Release buffer of RX message int release_retval = ipc->rx_release(ipc, recv_msg); if (release_retval != 0) { LOG_E(NFAPI_VNF, "%s error: release RX buffer failed Error: %d\n", __FUNCTION__, release_retval); return release_retval; } return 0; } void *epoll_recv_task(void *arg) { UNUSED(arg); struct epoll_event ev, events[MAX_EVENTS]; LOG_D(NFAPI_VNF,"Aerial recv task start \n"); int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { LOG_E(NFAPI_VNF, "%s epoll_create failed\n", __func__); } int ipc_rx_event_fd = ipc->get_fd(ipc); ev.events = EPOLLIN; ev.data.fd = ipc_rx_event_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { LOG_E(NFAPI_VNF, "%s epoll_ctl failed\n", __func__); } // From here on out the thread is ready to receive data, simulate the reception // of a PARAM.response to get the VNF to send a CONFIG.request nfapi_nr_param_response_scf_t resp_msg = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE}; nfapi_vnf_config_t * vnf_config = get_config(); vnf_config->nr_param_resp(vnf_config, 1, &resp_msg); while (((vnf_t *)vnf_config)->terminate == false) { LOG_D(NFAPI_VNF, "%s: epoll_wait fd_rx=%d ...\n", __func__, ipc_rx_event_fd); int nfds; do { // epoll_wait() may return EINTR when get unexpected signal SIGSTOP from system nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); } while (nfds == -1 && errno == EINTR); if (nfds < 0) { LOG_E(NFAPI_VNF, "epoll_wait failed: epoll_fd=%d nfds=%d err=%d - %s\n", epoll_fd, nfds, errno, strerror(errno)); } int n = 0; for (n = 0; n < nfds; ++n) { if (events[n].data.fd == ipc_rx_event_fd) { ipc->get_value(ipc); nv_ipc_msg_t recv_msg; while (aerial_recv_msg(&recv_msg) == 0); } } } close(epoll_fd); ipc->ipc_destroy(ipc); return NULL; } void create_recv_thread(int8_t affinity) { pthread_t thread_id; threadCreate(&thread_id, epoll_recv_task, NULL, "vnf_nvipc_aerial", affinity, OAI_PRIORITY_RT_MAX); } int load_hard_code_config(nv_ipc_config_t *config, int module_type, nv_ipc_transport_t _transport) { // Create configuration config->ipc_transport = _transport; if (set_nv_ipc_default_config(config, module_type) < 0) { LOG_E(NFAPI_VNF, "%s: set configuration failed\n", __func__); return -1; } int test_cuda_device_id = -1; LOG_D(NFAPI_VNF,"CUDA device ID configured : %d \n", test_cuda_device_id); config->transport_config.shm.cuda_device_id = test_cuda_device_id; if (test_cuda_device_id >= 0) { config->transport_config.shm.mempool_size[NV_IPC_MEMPOOL_CUDA_DATA].pool_len = 128; config->transport_config.shm.mempool_size[NV_IPC_MEMPOOL_CPU_DATA].pool_len = 1024; config->transport_config.shm.mempool_size[NV_IPC_MEMPOOL_CPU_MSG].pool_len = 4096; } return 0; } int nvIPC_Init(nvipc_params_t nvipc_params_s) { // Want to use transport SHM, type epoll, module secondary (reads the created shm from cuphycontroller) load_hard_code_config(&nv_ipc_config, NV_IPC_MODULE_SECONDARY, NV_IPC_TRANSPORT_SHM); // save the mempool sizes for later use in packing cpu_msg_buf_size = nv_ipc_get_buf_size(&nv_ipc_config, NV_IPC_MEMPOOL_CPU_MSG); cpu_data_buf_size = nv_ipc_get_buf_size(&nv_ipc_config, NV_IPC_MEMPOOL_CPU_DATA); cpu_large_buf_size = nv_ipc_get_buf_size(&nv_ipc_config, NV_IPC_MEMPOOL_CPU_LARGE); // Create nv_ipc_t instance LOG_I(NFAPI_VNF, "%s: creating IPC interface with prefix %s\n", __func__, nvipc_params_s.nvipc_shm_prefix); strcpy(nv_ipc_config.transport_config.shm.prefix, nvipc_params_s.nvipc_shm_prefix); if ((ipc = create_nv_ipc_interface(&nv_ipc_config)) == NULL) { LOG_E(NFAPI_VNF, "%s: create IPC interface failed\n", __func__); return -1; } LOG_I(NFAPI_VNF, "%s: create IPC interface successful\n", __func__); sleep(1); create_recv_thread(nvipc_params_s.nvipc_poll_core); return 0; } int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req) { nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_tti_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! ul_tti_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST; bool retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_tti_req->header); if (!retval) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } else { // Reset number of PDUs so that it is not resent ul_tti_req->n_pdus = 0; ul_tti_req->n_group = 0; ul_tti_req->n_ulcch = 0; ul_tti_req->n_ulsch = 0; } return retval; } int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req) { nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_dci_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! ul_dci_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST; bool retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_dci_req->header); if (!retval) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } else { ul_dci_req->numPdus = 0; } return retval; } int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req) { nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); tx_data_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! tx_data_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST; bool retval = p7_config->send_p7_msg(get_p7_vnf(), &tx_data_req->header); if (!retval) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } else { tx_data_req->Number_of_PDUs = 0; } return retval; } int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); dl_config_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST; dl_config_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! bool retval = p7_config->send_p7_msg(get_p7_vnf(), &dl_config_req->header); dl_config_req->dl_tti_request_body.nPDUs = 0; dl_config_req->dl_tti_request_body.nGroup = 0; if (!retval) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } return retval; } int oai_fapi_send_end_request(uint32_t frame, uint32_t slot) { nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); nfapi_nr_slot_indication_scf_t nr_slot_resp = {.header.message_id = 0x8F, .sfn = frame, .slot = slot}; bool retval = p7_config->send_p7_msg(get_p7_vnf(), &nr_slot_resp.header); if (!retval) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } return retval; }