zephyr/modules/nrf_wifi/bus/ipc_service.c
Chaitanya Tata 3953bb9ce3 nrf_wifi: Add nRF71 support
nRF7120 PDK support that uses IPC as comms b/w APP and Wi-Fi domains.

Signed-off-by: Chaitanya Tata <Chaitanya.Tata@nordicsemi.no>
2025-05-06 15:32:11 +02:00

206 lines
5.3 KiB
C

/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief File containing API definitions for the
* IPC service layer of the nrf71 Wi-Fi driver.
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(wifi_nrf_bus, CONFIG_WIFI_NRF70_BUSLIB_LOG_LEVEL);
#include "ipc_service.h"
static void wifi_ipc_ep_bound(void *priv)
{
wifi_ipc_t *context = (wifi_ipc_t *)priv;
context->busy_q.ipc_ready = true;
}
static void wifi_ipc_recv_callback(const void *data, size_t len, void *priv)
{
(void)len;
uint32_t global_addr = *((uint32_t *)data);
wifi_ipc_t *context = (wifi_ipc_t *)priv;
context->busy_q.recv_cb((void *)global_addr, context->busy_q.priv);
if (context->free_q != NULL) {
while (!spsc32_push(context->free_q, global_addr)) {
};
}
}
static void wifi_ipc_busyq_init(wifi_ipc_busyq_t *busyq, const ipc_device_wrapper_t *ipc_inst,
void *rx_cb, void *priv)
{
busyq->ipc_inst = ipc_inst;
busyq->ipc_ep_cfg.cb.bound = wifi_ipc_ep_bound;
busyq->ipc_ep_cfg.cb.received = wifi_ipc_recv_callback;
busyq->recv_cb = rx_cb;
busyq->ipc_ready = false;
busyq->priv = priv;
}
/**
* Register the IPC service on the busy_queue
*/
static wifi_ipc_status_t wifi_ipc_busyq_register(wifi_ipc_t *context)
{
int ret;
const struct device *ipc_instance = GET_IPC_INSTANCE(context->busy_q.ipc_inst);
ret = ipc_service_open_instance(ipc_instance);
if (ret < 0) {
return WIFI_IPC_STATUS_INIT_ERR;
}
context->busy_q.ipc_ep_cfg.name = "ep";
context->busy_q.ipc_ep_cfg.priv = context;
ret = ipc_service_register_endpoint(ipc_instance, &context->busy_q.ipc_ep,
&context->busy_q.ipc_ep_cfg);
if (ret < 0 && ret != -EALREADY) {
return WIFI_IPC_STATUS_INIT_ERR;
}
LOG_INF("IPC busy queue registered");
return WIFI_IPC_STATUS_OK;
}
wifi_ipc_status_t wifi_ipc_bind_ipc_service(wifi_ipc_t *context,
const ipc_device_wrapper_t *ipc_inst,
void (*rx_cb)(void *data, void *priv), void *priv)
{
wifi_ipc_busyq_init(&context->busy_q, ipc_inst, rx_cb, priv);
return wifi_ipc_busyq_register(context);
}
wifi_ipc_status_t wifi_ipc_bind_ipc_service_tx_rx(wifi_ipc_t *tx, wifi_ipc_t *rx,
const ipc_device_wrapper_t *ipc_inst,
void (*rx_cb)(void *data, void *priv), void *priv)
{
wifi_ipc_busyq_init(&rx->busy_q, ipc_inst, rx_cb, priv);
/**
* When initialising an IPC service, both TX and RX mailboxes need to be
* registered at the same time using a single function call. Both tx and
* rx need to refer to the same IPC instance.
*/
tx->linked_ipc = &rx->busy_q;
return wifi_ipc_busyq_register(rx);
}
wifi_ipc_status_t wifi_ipc_freeq_get(wifi_ipc_t *context, uint32_t *data)
{
if (context->free_q == NULL) {
LOG_ERR("Free queue is not initialised");
return WIFI_IPC_STATUS_FREEQ_UNINIT_ERR;
}
if (spsc32_is_empty(context->free_q)) {
LOG_DBG("Free queue is empty");
return WIFI_IPC_STATUS_FREEQ_EMPTY;
}
if (!spsc32_read_head(context->free_q, data)) {
LOG_DBG("Free queue is empty");
return WIFI_IPC_STATUS_FREEQ_EMPTY;
}
return WIFI_IPC_STATUS_OK;
}
wifi_ipc_status_t wifi_ipc_freeq_send(wifi_ipc_t *context, uint32_t data)
{
return (spsc32_push(context->free_q, data) == true ? WIFI_IPC_STATUS_OK
: WIFI_IPC_STATUS_FREEQ_FULL);
}
wifi_ipc_status_t wifi_ipc_busyq_send(wifi_ipc_t *context, uint32_t *data)
{
/* Get correct linked endpoint */
wifi_ipc_busyq_t *busyq =
context->linked_ipc ? context->linked_ipc : &context->busy_q;
if (!busyq->ipc_ready) {
LOG_ERR("IPC service is not ready");
return WIFI_IPC_STATUS_BUSYQ_NOTREADY;
}
int ret = ipc_service_send(&busyq->ipc_ep, data, sizeof(*data));
if (ret == -ENOMEM) {
LOG_ERR("No space in the buffer");
/* No space in the buffer */
return WIFI_IPC_STATUS_BUSYQ_FULL;
} else if (ret < 0) {
LOG_ERR("Critical IPC failure: %d", ret);
/* Critical IPC failure */
return WIFI_IPC_STATUS_BUSYQ_CRITICAL_ERR;
}
if (context->free_q != NULL) {
/* Free up global address pointer from the free queue */
uint32_t data_out;
return (spsc32_pop(context->free_q, &data_out) == true
? (*data == data_out ? WIFI_IPC_STATUS_OK
: WIFI_IPC_STATUS_FREEQ_INVALID)
: WIFI_IPC_STATUS_FREEQ_EMPTY);
}
return WIFI_IPC_STATUS_OK;
}
wifi_ipc_status_t wifi_ipc_host_cmd_init(wifi_ipc_t *context, uint32_t addr_freeq)
{
context->free_q = (void *)addr_freeq;
return WIFI_IPC_STATUS_OK;
}
wifi_ipc_status_t wifi_ipc_host_event_init(wifi_ipc_t *context, uint32_t addr_freeq)
{
context->free_q = (void *)addr_freeq;
return WIFI_IPC_STATUS_OK;
}
wifi_ipc_status_t wifi_ipc_host_cmd_get(wifi_ipc_t *context, uint32_t *data)
{
return wifi_ipc_freeq_get(context, data);
}
wifi_ipc_status_t wifi_ipc_host_cmd_send(wifi_ipc_t *context, uint32_t *data)
{
return wifi_ipc_busyq_send(context, data);
}
wifi_ipc_status_t wifi_ipc_host_cmd_send_memcpy(wifi_ipc_t *context, const void *msg,
size_t len)
{
int ret;
uint32_t gdram_addr;
ret = wifi_ipc_host_cmd_get(context, &gdram_addr);
if (ret != WIFI_IPC_STATUS_OK) {
LOG_ERR("Failed to get command location from free queue: %d", ret);
return ret;
}
memcpy((void *)gdram_addr, msg, len);
return wifi_ipc_host_cmd_send(context, &gdram_addr);
}
wifi_ipc_status_t wifi_ipc_host_tx_send(wifi_ipc_t *context, const void *msg)
{
return wifi_ipc_host_cmd_send(context, (uint32_t *)&msg);
}