blob: 94d53e2a2761fa85ce51d315442a15d45e4dacb8 [file] [log] [blame]
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief File containing netowrk stack interface specific definitions for the
* Zephyr OS layer of the Wi-Fi driver.
*/
#include <stdlib.h>
#ifdef CONFIG_WIFI_RANDOM_MAC_ADDRESS
#include <zephyr/random/random.h>
#endif
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL);
#include "net_private.h"
#include "util.h"
#include "fmac_api.h"
#include "fmac_util.h"
#include "shim.h"
#include "fmac_main.h"
#include "wpa_supp_if.h"
#include "net_if.h"
extern char *net_sprint_ll_addr_buf(const uint8_t *ll, uint8_t ll_len,
char *buf, int buflen);
#ifdef CONFIG_NRF70_STA_MODE
static struct net_if_mcast_monitor mcast_monitor;
#endif /* CONFIG_NRF70_STA_MODE */
void nrf_wifi_set_iface_event_handler(void *os_vif_ctx,
struct nrf_wifi_umac_event_set_interface *event,
unsigned int event_len)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
if (!os_vif_ctx) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
if (!event) {
LOG_ERR("%s: event is NULL",
__func__);
goto out;
}
(void)event_len;
vif_ctx_zep = os_vif_ctx;
vif_ctx_zep->set_if_event_received = true;
vif_ctx_zep->set_if_status = event->return_value;
out:
return;
}
#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY
static void nrf_wifi_rpu_recovery_work_handler(struct k_work *work)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = CONTAINER_OF(work,
struct nrf_wifi_vif_ctx_zep,
nrf_wifi_rpu_recovery_work);
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
return;
}
if (!vif_ctx_zep->zep_net_if_ctx) {
LOG_ERR("%s: zep_net_if_ctx is NULL", __func__);
return;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
return;
}
k_mutex_lock(&rpu_ctx_zep->rpu_lock, K_FOREVER);
LOG_DBG("%s: Bringing the interface down", __func__);
/* This indirectly does a cold-boot of RPU */
net_if_down(vif_ctx_zep->zep_net_if_ctx);
k_msleep(CONFIG_NRF_WIFI_RPU_RECOVERY_PROPAGATION_DELAY_MS);
LOG_DBG("%s: Bringing the interface up", __func__);
net_if_up(vif_ctx_zep->zep_net_if_ctx);
k_mutex_unlock(&rpu_ctx_zep->rpu_lock);
}
void nrf_wifi_rpu_recovery_cb(void *vif_ctx_handle,
void *event_data,
unsigned int event_len)
{
struct nrf_wifi_fmac_vif_ctx *vif_ctx = vif_ctx_handle;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
if (!vif_ctx) {
LOG_ERR("%s: vif_ctx is NULL",
__func__);
goto out;
}
fmac_dev_ctx = vif_ctx->fmac_dev_ctx;
def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
if (!def_dev_ctx) {
LOG_ERR("%s: def_dev_ctx is NULL",
__func__);
goto out;
}
vif_ctx_zep = vif_ctx->os_vif_ctx;
(void)event_data;
k_work_submit(&vif_ctx_zep->nrf_wifi_rpu_recovery_work);
out:
return;
}
#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */
#ifdef CONFIG_NRF70_DATA_TX
static void nrf_wifi_net_iface_work_handler(struct k_work *work)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = CONTAINER_OF(work,
struct nrf_wifi_vif_ctx_zep,
nrf_wifi_net_iface_work);
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
return;
}
if (!vif_ctx_zep->zep_net_if_ctx) {
LOG_ERR("%s: zep_net_if_ctx is NULL", __func__);
return;
}
if (vif_ctx_zep->if_carr_state == NRF_WIFI_FMAC_IF_CARR_STATE_ON) {
net_if_dormant_off(vif_ctx_zep->zep_net_if_ctx);
} else if (vif_ctx_zep->if_carr_state == NRF_WIFI_FMAC_IF_CARR_STATE_OFF) {
net_if_dormant_on(vif_ctx_zep->zep_net_if_ctx);
}
}
#if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX)
void nrf_wifi_if_sniffer_rx_frm(void *os_vif_ctx, void *frm,
struct raw_rx_pkt_header *raw_rx_hdr,
bool pkt_free)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = os_vif_ctx;
struct net_if *iface = vif_ctx_zep->zep_net_if_ctx;
struct net_pkt *pkt;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
int ret;
def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
pkt = net_raw_pkt_from_nbuf(iface, frm, sizeof(struct raw_rx_pkt_header),
raw_rx_hdr,
pkt_free);
if (!pkt) {
LOG_DBG("Failed to allocate net_pkt");
return;
}
net_capture_pkt(iface, pkt);
ret = net_recv_data(iface, pkt);
if (ret < 0) {
LOG_DBG("RCV Packet dropped by NET stack: %d", ret);
net_pkt_unref(pkt);
}
}
#endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */
void nrf_wifi_if_rx_frm(void *os_vif_ctx, void *frm)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = os_vif_ctx;
struct net_if *iface = vif_ctx_zep->zep_net_if_ctx;
struct net_pkt *pkt;
int status;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
struct rpu_host_stats *host_stats = NULL;
def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
host_stats = &def_dev_ctx->host_stats;
host_stats->total_rx_pkts++;
pkt = net_pkt_from_nbuf(iface, frm);
if (!pkt) {
LOG_DBG("Failed to allocate net_pkt");
host_stats->total_rx_drop_pkts++;
return;
}
status = net_recv_data(iface, pkt);
if (status < 0) {
LOG_DBG("RCV Packet dropped by NET stack: %d", status);
host_stats->total_rx_drop_pkts++;
net_pkt_unref(pkt);
}
}
enum nrf_wifi_status nrf_wifi_if_carr_state_chg(void *os_vif_ctx,
enum nrf_wifi_fmac_if_carr_state carr_state)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
if (!os_vif_ctx) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
vif_ctx_zep = os_vif_ctx;
vif_ctx_zep->if_carr_state = carr_state;
LOG_DBG("%s: Carrier state: %d", __func__, carr_state);
k_work_submit(&vif_ctx_zep->nrf_wifi_net_iface_work);
status = NRF_WIFI_STATUS_SUCCESS;
out:
return status;
}
static bool is_eapol(struct net_pkt *pkt)
{
struct net_eth_hdr *hdr;
uint16_t ethertype;
hdr = NET_ETH_HDR(pkt);
ethertype = ntohs(hdr->type);
return ethertype == NET_ETH_PTYPE_EAPOL;
}
#endif /* CONFIG_NRF70_DATA_TX */
enum ethernet_hw_caps nrf_wifi_if_caps_get(const struct device *dev)
{
enum ethernet_hw_caps caps = (ETHERNET_LINK_10BASE_T |
ETHERNET_LINK_100BASE_T | ETHERNET_LINK_1000BASE_T);
#ifdef CONFIG_NRF70_TCP_IP_CHECKSUM_OFFLOAD
caps |= ETHERNET_HW_TX_CHKSUM_OFFLOAD |
ETHERNET_HW_RX_CHKSUM_OFFLOAD;
#endif /* CONFIG_NRF70_TCP_IP_CHECKSUM_OFFLOAD */
#ifdef CONFIG_NRF70_RAW_DATA_TX
caps |= ETHERNET_TXINJECTION_MODE;
#endif
#ifdef CONFIG_NRF70_PROMISC_DATA_RX
caps |= ETHERNET_PROMISC_MODE;
#endif
return caps;
}
int nrf_wifi_if_send(const struct device *dev,
struct net_pkt *pkt)
{
int ret = -1;
#ifdef CONFIG_NRF70_DATA_TX
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
struct rpu_host_stats *host_stats = NULL;
void *nbuf = NULL;
if (!dev || !pkt) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
goto unlock;
}
def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx);
host_stats = &def_dev_ctx->host_stats;
nbuf = net_pkt_to_nbuf(pkt);
if (!nbuf) {
LOG_DBG("Failed to allocate net_pkt");
host_stats->total_tx_drop_pkts++;
goto out;
}
#ifdef CONFIG_NRF70_RAW_DATA_TX
if ((*(unsigned int *)pkt->frags->data) == NRF_WIFI_MAGIC_NUM_RAWTX) {
if (vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) {
goto unlock;
}
ret = nrf_wifi_fmac_start_rawpkt_xmit(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
nbuf);
} else {
#endif /* CONFIG_NRF70_RAW_DATA_TX */
if ((vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) ||
(!vif_ctx_zep->authorized && !is_eapol(pkt))) {
goto unlock;
}
ret = nrf_wifi_fmac_start_xmit(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
nbuf);
#ifdef CONFIG_NRF70_RAW_DATA_TX
}
#endif /* CONFIG_NRF70_RAW_DATA_TX */
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
#else
ARG_UNUSED(dev);
ARG_UNUSED(pkt);
goto out;
#endif /* CONFIG_NRF70_DATA_TX */
out:
return ret;
}
#ifdef CONFIG_NRF70_STA_MODE
static void ip_maddr_event_handler(struct net_if *iface,
const struct net_addr *addr, bool is_joined)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct net_eth_addr mac_addr;
struct nrf_wifi_umac_mcast_cfg *mcast_info = NULL;
enum nrf_wifi_status status;
uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")];
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
int ret;
vif_ctx_zep = nrf_wifi_get_vif_ctx(iface);
if (!vif_ctx_zep) {
return;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
mcast_info = k_calloc(sizeof(*mcast_info), sizeof(char));
if (!mcast_info) {
LOG_ERR("%s: Unable to allocate memory of size %d "
"for mcast_info", __func__, sizeof(*mcast_info));
return;
}
switch (addr->family) {
case AF_INET:
net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &mac_addr);
break;
case AF_INET6:
net_eth_ipv6_mcast_to_mac_addr(&addr->in6_addr, &mac_addr);
break;
default:
LOG_ERR("%s: Invalid address family %d", __func__, addr->family);
break;
}
if (is_joined) {
mcast_info->type = MCAST_ADDR_ADD;
} else {
mcast_info->type = MCAST_ADDR_DEL;
}
memcpy(((char *)(mcast_info->mac_addr)),
&mac_addr,
NRF_WIFI_ETH_ADDR_LEN);
status = nrf_wifi_fmac_set_mcast_addr(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
mcast_info);
if (status == NRF_WIFI_STATUS_FAIL) {
LOG_ERR("%s: nrf_wifi_fmac_set_multicast failed for"
" mac addr=%s",
__func__,
net_sprint_ll_addr_buf(mac_addr.addr,
WIFI_MAC_ADDR_LEN, mac_string_buf,
sizeof(mac_string_buf)));
}
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
k_free(mcast_info);
}
#endif /* CONFIG_NRF70_STA_MODE */
#ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED
BUILD_ASSERT(sizeof(CONFIG_WIFI_FIXED_MAC_ADDRESS) - 1 == ((WIFI_MAC_ADDR_LEN * 2) + 5),
"Invalid fixed MAC address length");
#endif
enum nrf_wifi_status nrf_wifi_get_mac_addr(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
int ret;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
#ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED
char fixed_mac_addr[WIFI_MAC_ADDR_LEN];
ret = net_bytes_from_str(fixed_mac_addr,
WIFI_MAC_ADDR_LEN,
CONFIG_WIFI_FIXED_MAC_ADDRESS);
if (ret < 0) {
LOG_ERR("%s: Failed to parse MAC address: %s",
__func__,
CONFIG_WIFI_FIXED_MAC_ADDRESS);
goto unlock;
}
memcpy(vif_ctx_zep->mac_addr.addr,
fixed_mac_addr,
WIFI_MAC_ADDR_LEN);
#elif CONFIG_WIFI_RANDOM_MAC_ADDRESS
char random_mac_addr[WIFI_MAC_ADDR_LEN];
sys_rand_get(random_mac_addr, WIFI_MAC_ADDR_LEN);
random_mac_addr[0] = (random_mac_addr[0] & UNICAST_MASK) | LOCAL_BIT;
memcpy(vif_ctx_zep->mac_addr.addr,
random_mac_addr,
WIFI_MAC_ADDR_LEN);
#elif CONFIG_WIFI_OTP_MAC_ADDRESS
status = nrf_wifi_fmac_otp_mac_addr_get(fmac_dev_ctx,
vif_ctx_zep->vif_idx,
vif_ctx_zep->mac_addr.addr);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: Fetching of MAC address from OTP failed",
__func__);
goto unlock;
}
#endif
if (!nrf_wifi_utils_is_mac_addr_valid(vif_ctx_zep->mac_addr.addr)) {
LOG_ERR("%s: Invalid MAC address: %s",
__func__,
net_sprint_ll_addr(vif_ctx_zep->mac_addr.addr,
WIFI_MAC_ADDR_LEN));
status = NRF_WIFI_STATUS_FAIL;
memset(vif_ctx_zep->mac_addr.addr,
0,
WIFI_MAC_ADDR_LEN);
goto unlock;
}
status = NRF_WIFI_STATUS_SUCCESS;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
return status;
}
void nrf_wifi_if_init_zep(struct net_if *iface)
{
const struct device *dev = NULL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct ethernet_context *eth_ctx = net_if_l2_data(iface);
if (!iface) {
LOG_ERR("%s: Invalid parameters",
__func__);
return;
}
dev = net_if_get_device(iface);
if (!dev) {
LOG_ERR("%s: Invalid dev",
__func__);
return;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
return;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep) {
LOG_ERR("%s: rpu_ctx_zep is NULL",
__func__);
return;
}
vif_ctx_zep->zep_net_if_ctx = iface;
vif_ctx_zep->zep_dev_ctx = dev;
eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
ethernet_init(iface);
net_eth_carrier_on(iface);
net_if_dormant_on(iface);
#ifdef CONFIG_NRF70_STA_MODE
net_if_mcast_mon_register(&mcast_monitor, iface, ip_maddr_event_handler);
#endif /* CONFIG_NRF70_STA_MODE */
#ifdef CONFIG_NRF70_DATA_TX
k_work_init(&vif_ctx_zep->nrf_wifi_net_iface_work,
nrf_wifi_net_iface_work_handler);
#endif /* CONFIG_NRF70_DATA_TX */
#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY
k_work_init(&vif_ctx_zep->nrf_wifi_rpu_recovery_work,
nrf_wifi_rpu_recovery_work_handler);
#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */
#if !defined(CONFIG_NRF_WIFI_IF_AUTO_START)
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
#endif /* CONFIG_NRF_WIFI_IF_AUTO_START */
net_if_flag_set(iface, NET_IF_NO_TX_LOCK);
}
/* Board-specific Wi-Fi startup code to run before the Wi-Fi device is started */
__weak int nrf_wifi_if_zep_start_board(const struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
/* Board-specific Wi-Fi shutdown code to run after the Wi-Fi device is stopped */
__weak int nrf_wifi_if_zep_stop_board(const struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
int nrf_wifi_if_start_zep(const struct device *dev)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
struct nrf_wifi_umac_chg_vif_state_info vif_info;
struct nrf_wifi_umac_add_vif_info add_vif_info;
char *mac_addr = NULL;
unsigned int mac_addr_len = 0;
int ret = -1;
bool fmac_dev_added = false;
if (!dev) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
if (!device_is_ready(dev)) {
LOG_ERR("%s: Device %s is not ready",
__func__, dev->name);
goto out;
}
ret = nrf_wifi_if_zep_start_board(dev);
if (ret) {
LOG_ERR("nrf_wifi_if_zep_start_board failed with error: %d",
ret);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep) {
LOG_ERR("%s: rpu_ctx_zep is NULL",
__func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
if (!rpu_ctx_zep->rpu_ctx) {
status = nrf_wifi_fmac_dev_add_zep(&rpu_drv_priv_zep);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_dev_add_zep failed",
__func__);
goto out;
}
fmac_dev_added = true;
LOG_DBG("%s: FMAC device added", __func__);
}
fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
memset(&add_vif_info,
0,
sizeof(add_vif_info));
/* TODO: This should be configurable */
add_vif_info.iftype = NRF_WIFI_IFTYPE_STATION;
memcpy(add_vif_info.ifacename,
dev->name,
strlen(dev->name));
vif_ctx_zep->vif_idx = nrf_wifi_fmac_add_vif(fmac_dev_ctx,
vif_ctx_zep,
&add_vif_info);
if (vif_ctx_zep->vif_idx >= MAX_NUM_VIFS) {
LOG_ERR("%s: FMAC returned invalid interface index",
__func__);
goto dev_rem;
}
k_mutex_init(&vif_ctx_zep->vif_lock);
rpu_ctx_zep->vif_ctx_zep[vif_ctx_zep->vif_idx].if_type =
add_vif_info.iftype;
/* Check if user has provided a valid MAC address, if not
* fetch it from OTP.
*/
mac_addr = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->addr;
mac_addr_len = net_if_get_link_addr(vif_ctx_zep->zep_net_if_ctx)->len;
if (!nrf_wifi_utils_is_mac_addr_valid(mac_addr)) {
status = nrf_wifi_get_mac_addr(vif_ctx_zep);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: Failed to get MAC address",
__func__);
goto del_vif;
}
net_if_set_link_addr(vif_ctx_zep->zep_net_if_ctx,
vif_ctx_zep->mac_addr.addr,
WIFI_MAC_ADDR_LEN,
NET_LINK_ETHERNET);
mac_addr = vif_ctx_zep->mac_addr.addr;
mac_addr_len = WIFI_MAC_ADDR_LEN;
}
status = nrf_wifi_fmac_set_vif_macaddr(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
mac_addr);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: MAC address change failed",
__func__);
goto del_vif;
}
memset(&vif_info,
0,
sizeof(vif_info));
vif_info.state = NRF_WIFI_FMAC_IF_OP_STATE_UP;
vif_info.if_index = vif_ctx_zep->vif_idx;
memcpy(vif_ctx_zep->ifname,
dev->name,
strlen(dev->name));
status = nrf_wifi_fmac_chg_vif_state(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
&vif_info);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed",
__func__);
goto del_vif;
}
#ifdef CONFIG_NRF70_STA_MODE
nrf_wifi_wpa_supp_event_mac_chgd(vif_ctx_zep);
#ifdef CONFIG_NRF_WIFI_LOW_POWER
status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
NRF_WIFI_PS_ENABLED);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_set_power_save failed",
__func__);
goto dev_rem;
}
#endif /* CONFIG_NRF_WIFI_LOW_POWER */
#endif /* CONFIG_NRF70_STA_MODE */
vif_ctx_zep->if_op_state = NRF_WIFI_FMAC_IF_OP_STATE_UP;
ret = 0;
goto out;
del_vif:
status = nrf_wifi_fmac_del_vif(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_del_vif failed",
__func__);
}
dev_rem:
/* Free only if we added above i.e., for 1st VIF */
if (fmac_dev_added) {
nrf_wifi_fmac_dev_rem_zep(&rpu_drv_priv_zep);
}
out:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
return ret;
}
int nrf_wifi_if_stop_zep(const struct device *dev)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_umac_chg_vif_state_info vif_info;
int ret = -1;
if (!dev) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep) {
LOG_ERR("%s: rpu_ctx_zep is NULL",
__func__);
goto out;
}
#ifdef CONFIG_NRF70_STA_MODE
#ifdef CONFIG_NRF_WIFI_LOW_POWER
status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
NRF_WIFI_PS_DISABLED);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_set_power_save failed",
__func__);
goto unlock;
}
#endif /* CONFIG_NRF_WIFI_LOW_POWER */
#endif /* CONFIG_NRF70_STA_MODE */
memset(&vif_info,
0,
sizeof(vif_info));
vif_info.state = NRF_WIFI_FMAC_IF_OP_STATE_DOWN;
vif_info.if_index = vif_ctx_zep->vif_idx;
status = nrf_wifi_fmac_chg_vif_state(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx,
&vif_info);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed",
__func__);
goto unlock;
}
status = nrf_wifi_fmac_del_vif(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_del_vif failed",
__func__);
goto unlock;
}
vif_ctx_zep->if_op_state = NRF_WIFI_FMAC_IF_OP_STATE_DOWN;
if (nrf_wifi_fmac_get_num_vifs(rpu_ctx_zep->rpu_ctx) == 0) {
nrf_wifi_fmac_dev_rem_zep(&rpu_drv_priv_zep);
}
ret = 0;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
ret = nrf_wifi_if_zep_stop_board(dev);
if (ret) {
LOG_ERR("nrf_wifi_if_zep_stop_board failed with error: %d",
ret);
}
out:
return ret;
}
int nrf_wifi_if_get_config_zep(const struct device *dev,
enum ethernet_config_type type,
struct ethernet_config *config)
{
int ret = -1;
#ifdef CONFIG_NRF70_RAW_DATA_TX
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
if (!dev || !config) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
if (!def_dev_ctx) {
LOG_ERR("%s: def_dev_ctx is NULL",
__func__);
goto unlock;
}
if (type == ETHERNET_CONFIG_TYPE_TXINJECTION_MODE) {
config->txinjection_mode =
def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode;
}
ret = 0;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
#endif
return ret;
}
int nrf_wifi_if_set_config_zep(const struct device *dev,
enum ethernet_config_type type,
const struct ethernet_config *config)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
int ret = -1;
if (!dev) {
LOG_ERR("%s: Invalid parameters",
__func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL",
__func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
/* Commands without FMAC interaction */
if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) {
if (!net_eth_is_addr_valid((struct net_eth_addr *)&config->mac_address)) {
LOG_ERR("%s: Invalid MAC address",
__func__);
goto unlock;
}
memcpy(vif_ctx_zep->mac_addr.addr,
config->mac_address.addr,
sizeof(vif_ctx_zep->mac_addr.addr));
net_if_set_link_addr(vif_ctx_zep->zep_net_if_ctx,
vif_ctx_zep->mac_addr.addr,
sizeof(vif_ctx_zep->mac_addr.addr),
NET_LINK_ETHERNET);
ret = 0;
goto unlock;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
if (!def_dev_ctx) {
LOG_ERR("%s: def_dev_ctx is NULL",
__func__);
goto unlock;
}
#ifdef CONFIG_NRF70_RAW_DATA_TX
if (type == ETHERNET_CONFIG_TYPE_TXINJECTION_MODE) {
unsigned char mode;
if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode ==
config->txinjection_mode) {
LOG_INF("%s: Driver TX injection setting is same as configured setting",
__func__);
goto unlock;
}
/**
* Since UMAC wishes to use the same mode command as previously
* used for mode, `OR` the primary mode with TX-Injection mode and
* send it to the UMAC. That way UMAC can still maintain code
* as is
*/
if (config->txinjection_mode) {
mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) |
(NRF_WIFI_TX_INJECTION_MODE);
} else {
mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) ^
(NRF_WIFI_TX_INJECTION_MODE);
}
ret = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx, mode);
if (ret != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: Mode set operation failed", __func__);
goto unlock;
}
}
#endif
#ifdef CONFIG_NRF70_PROMISC_DATA_RX
else if (type == ETHERNET_CONFIG_TYPE_PROMISC_MODE) {
unsigned char mode;
if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->promisc_mode ==
config->promisc_mode) {
LOG_ERR("%s: Driver promisc mode setting is same as configured setting",
__func__);
goto out;
}
if (config->promisc_mode) {
mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) |
(NRF_WIFI_PROMISCUOUS_MODE);
} else {
mode = (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) ^
(NRF_WIFI_PROMISCUOUS_MODE);
}
ret = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx,
vif_ctx_zep->vif_idx, mode);
if (ret != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: mode set operation failed", __func__);
goto out;
}
}
#endif
ret = 0;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
return ret;
}
#ifdef CONFIG_NET_STATISTICS_ETHERNET
struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev)
{
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
if (!dev) {
LOG_ERR("%s Device not found", __func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
goto out;
}
return &vif_ctx_zep->eth_stats;
out:
return NULL;
}
#endif /* CONFIG_NET_STATISTICS_ETHERNET */
#ifdef CONFIG_NET_STATISTICS_WIFI
int nrf_wifi_stats_get(const struct device *dev, struct net_stats_wifi *zstats)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
#ifdef CONFIG_NRF70_RAW_DATA_TX
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
#endif /* CONFIG_NRF70_RAW_DATA_TX */
struct rpu_op_stats stats;
int ret = -1;
if (!dev) {
LOG_ERR("%s Device not found", __func__);
goto out;
}
if (!zstats) {
LOG_ERR("%s Stats buffer not allocated", __func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
memset(&stats, 0, sizeof(struct rpu_op_stats));
status = nrf_wifi_fmac_stats_get(rpu_ctx_zep->rpu_ctx, 0, &stats);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_stats_get failed", __func__);
goto unlock;
}
zstats->pkts.tx = stats.host.total_tx_pkts;
zstats->pkts.rx = stats.host.total_rx_pkts;
zstats->errors.tx = stats.host.total_tx_drop_pkts;
zstats->errors.rx = stats.host.total_rx_drop_pkts +
stats.fw.umac.interface_data_stats.rx_checksum_error_count;
zstats->bytes.received = stats.fw.umac.interface_data_stats.rx_bytes;
zstats->bytes.sent = stats.fw.umac.interface_data_stats.tx_bytes;
zstats->sta_mgmt.beacons_rx = stats.fw.umac.interface_data_stats.rx_beacon_success_count;
zstats->sta_mgmt.beacons_miss = stats.fw.umac.interface_data_stats.rx_beacon_miss_count;
zstats->broadcast.rx = stats.fw.umac.interface_data_stats.rx_broadcast_pkt_count;
zstats->broadcast.tx = stats.fw.umac.interface_data_stats.tx_broadcast_pkt_count;
zstats->multicast.rx = stats.fw.umac.interface_data_stats.rx_multicast_pkt_count;
zstats->multicast.tx = stats.fw.umac.interface_data_stats.tx_multicast_pkt_count;
zstats->unicast.rx = stats.fw.umac.interface_data_stats.rx_unicast_pkt_count;
zstats->unicast.tx = stats.fw.umac.interface_data_stats.tx_unicast_pkt_count;
#ifdef CONFIG_NRF70_RAW_DATA_TX
def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx);
zstats->errors.tx += def_dev_ctx->raw_pkt_stats.raw_pkt_send_failure;
#endif /* CONFIG_NRF70_RAW_DATA_TX */
ret = 0;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
return ret;
}
int nrf_wifi_stats_reset(const struct device *dev)
{
enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
int ret = -1;
if (!dev) {
LOG_ERR("%s Device not found", __func__);
goto out;
}
vif_ctx_zep = dev->data;
if (!vif_ctx_zep) {
LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
goto out;
}
ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
if (ret != 0) {
LOG_ERR("%s: Failed to lock vif_lock", __func__);
goto out;
}
rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) {
LOG_DBG("%s: rpu_ctx_zep or rpu_ctx is NULL",
__func__);
goto unlock;
}
status = nrf_wifi_fmac_stats_reset(rpu_ctx_zep->rpu_ctx);
if (status != NRF_WIFI_STATUS_SUCCESS) {
LOG_ERR("%s: nrf_wifi_fmac_stats_reset failed", __func__);
goto unlock;
}
def_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx);
memset(&def_dev_ctx->host_stats, 0, sizeof(struct rpu_host_stats));
ret = 0;
unlock:
k_mutex_unlock(&vif_ctx_zep->vif_lock);
out:
return ret;
}
#endif /* CONFIG_NET_STATISTICS_WIFI */