Part of the split. Please, take a look at the cover letter for more details Reviewed-by: Viktor Barna <viktor.barna.rj@xxxxxxxxxxxxxx> Reviewed-by: Gal Gur <gal.gur.jx@xxxxxxxxxxx> Signed-off-by: Alexander Savchenko <oleksandr.savchenko.dn@xxxxxxxxxxxxxx> --- drivers/net/wireless/renesas/ra6w/dev.c | 233 ++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 drivers/net/wireless/renesas/ra6w/dev.c diff --git a/drivers/net/wireless/renesas/ra6w/dev.c b/drivers/net/wireless/renesas/ra6w/dev.c new file mode 100644 index 000000000000..16716fb4fdd7 --- /dev/null +++ b/drivers/net/wireless/renesas/ra6w/dev.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file contains netdevice communication. + * + * Copyright (C) [2022-2025] Renesas Electronics Corporation and/or its affiliates. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <net/cfg80211.h> + +#include "core.h" +#include "cfg80211.h" +#include "dev.h" +#include "params.h" +#include "dbg.h" + +static int ra6w_dev_ndo_open(struct net_device *ndev) +{ + struct ra6w_cfg80211_vif *vif = netdev_priv(ndev); + struct ra6w_cfg80211_priv *priv = vif->priv; + + vif->up = true; + priv->vif_started++; + + if (ra6w_recovery_reprobe_get()) { + ra6w_recovery_reprobe_set(false); + return 0; + } + + netif_carrier_off(ndev); + + return 0; +} + +static int ra6w_dev_ndo_close(struct net_device *ndev) +{ + struct ra6w_cfg80211_vif *vif = netdev_priv(ndev); + struct ra6w_cfg80211_priv *priv = NULL; + + if (!vif) + return 0; + + priv = vif->priv; + if (!priv) + return 0; + + if (priv->scan_request) { + ra6w_ctrl_scan_cancel(&priv->core->ctrl, vif); + ra6w_cfg80211_scan_done(priv); + } + + vif->up = false; + if (netif_carrier_ok(ndev)) { + if (vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_P2P_CLIENT) + cfg80211_disconnected(ndev, WLAN_REASON_DEAUTH_LEAVING, + NULL, 0, true, GFP_ATOMIC); + + netif_carrier_off(ndev); + } + + if (vif->type == NL80211_IFTYPE_MONITOR) + priv->mon_vif_idx = RA6W_CFG80211_VIF_IDX_INVALID; + + priv->vif_started--; + + return 0; +} + +static struct ra6w_cfg80211_sta *ra6w_dev_get_sta(struct ra6w_cfg80211_vif *vif, + struct sk_buff *skb) +{ + struct ra6w_cfg80211_sta *sta = NULL; + + if (!vif) + return NULL; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + sta = vif->sta.ap; + if (sta && sta->valid) + return sta; + + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: { + const struct ethhdr *eth = (struct ethhdr *)skb->data; + + if (is_multicast_ether_addr(eth->h_dest)) { + sta = ra6w_cfg80211_sta_get(vif->priv, vif->ap.bcmc_index); + if (sta && sta->valid) + return sta; + + break; + } + + list_for_each_entry(sta, &vif->ap.sta_list, list) { + if (sta->valid && ether_addr_equal(sta->mac_addr, eth->h_dest)) + return sta; + } + } + break; + default: + break; + } + + return NULL; +} + +static netdev_tx_t ra6w_dev_tx(struct ra6w_cfg80211_vif *vif, struct sk_buff *skb) +{ + struct ra6w_cfg80211_priv *priv = vif->priv; + struct ra6w_tx *tx = &priv->core->tx; + struct ra6w_tx_buf *tx_buf = NULL; + const struct ra6w_cfg80211_sta *sta = NULL; + u8 hdr_size = RA6W_GET_DATA_SIZE(RA6W_TX_EXT_LEN, 0); + u8 tx_buf_ac = RA6W_TX_DATA_AC; + u8 sta_idx = RA6W_CFG80211_STA_IDX_INVALID; + u8 prio = skb->priority; + + if (skb->len - hdr_size > RA6W_CMD_DATA_SIZE) + return -EINVAL; + + sta = ra6w_dev_get_sta(vif, skb); + if (sta) + sta_idx = sta->sta_idx; + + if (sta_idx == RA6W_CFG80211_STA_IDX_INVALID) + return -ENXIO; + + if (skb_headroom(skb) < hdr_size) { + int ret; + + ret = pskb_expand_head(skb, hdr_size, 0, GFP_ATOMIC); + if (ret < 0) { + ra6w_err("[%s] SKB resize failed: hdr_size %u (reserved %u) ret %d\n", + __func__, hdr_size, skb_headroom(skb), ret); + + return -EFAULT; + } + } + + if (skb->priority == 0 || skb->priority > IEEE80211_QOS_CTL_TAG1D_MASK) + prio = cfg80211_classify8021d(skb, NULL); + + tx_buf = (struct ra6w_tx_buf *)skb_push(skb, hdr_size); + tx_buf->cmd = RA6W_CMD_DATA_TX; + tx_buf->ext_len = RA6W_TX_EXT_LEN; + tx_buf->ext_hdr.buf_idx = tx_buf_ac; + tx_buf->ext_hdr.tid = prio; + tx_buf->ext_hdr.vif_idx = vif->vif_idx; + tx_buf->ext_hdr.sta_idx = sta_idx; + tx_buf->ext_hdr.flags = 0; + tx_buf->ext_hdr.sn = 0; + tx_buf->data_len = cpu_to_le16(skb->len - hdr_size); + + return ra6w_tx_event_post(tx, tx_buf_ac, skb); +} + +static netdev_tx_t ra6w_dev_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret; + struct ra6w_cfg80211_vif *vif = netdev_priv(ndev); + + ret = ra6w_dev_tx(vif, skb); + if (ret) { + dev_kfree_skb(skb); + ndev->stats.tx_errors++; + ndev->stats.tx_dropped++; + + return NETDEV_TX_OK; + } + + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + + return NETDEV_TX_OK; +} + +static const struct net_device_ops ra6w_dev_ops = { + .ndo_open = ra6w_dev_ndo_open, + .ndo_stop = ra6w_dev_ndo_close, + .ndo_start_xmit = ra6w_dev_ndo_start_xmit, + .ndo_set_mac_address = eth_mac_addr, +}; + +static const struct net_device_ops ra6w_dev_monitor_ops = { + .ndo_open = ra6w_dev_ndo_open, + .ndo_stop = ra6w_dev_ndo_close, + .ndo_set_mac_address = eth_mac_addr, +}; + +static u32 ra6w_dev_ethtool_get_link(struct net_device *ndev) +{ + return netif_carrier_ok(ndev); +} + +static const struct ethtool_ops ra6w_ethtool_ops = { + .get_link = ra6w_dev_ethtool_get_link, + .get_drvinfo = cfg80211_get_drvinfo, +}; + +void ra6w_dev_init(struct net_device *ndev) +{ + if (!ndev) + return; + + ether_setup(ndev); + + ndev->priv_flags &= ~IFF_TX_SKB_SHARING; + + ra6w_dev_set_ops(ndev); + ndev->ethtool_ops = &ra6w_ethtool_ops; + + ndev->needs_free_netdev = true; + ndev->watchdog_timeo = RA6W_CMD_TX_LIFETIME_MS; + ndev->needed_headroom += RA6W_GET_DATA_SIZE(RA6W_TX_EXT_LEN, 0); + ndev->hw_features = 0; +} + +void ra6w_dev_set_ops(struct net_device *ndev) +{ + ndev->netdev_ops = &ra6w_dev_ops; + ndev->tx_queue_len = RA6W_TX_BUF_Q_MAX; +} + +void ra6w_dev_set_monitor_ops(struct net_device *ndev) +{ + ndev->netdev_ops = &ra6w_dev_monitor_ops; +} -- 2.17.1