diff mbox series

[14/38] ra6w: add dev.c

Message ID 20250417135236.52410-15-oleksandr.savchenko.dn@bp.renesas.com
State New
Headers show
Series wireless: ra6w driver for Renesas IEEE 802.11ax devices | expand

Commit Message

Alexander Savchenko April 17, 2025, 1:52 p.m. UTC
Part of the split. Please, take a look at the cover letter for more details

Reviewed-by: Viktor Barna <viktor.barna.rj@bp.renesas.com>
Reviewed-by: Gal Gur <gal.gur.jx@renesas.com>
Signed-off-by: Alexander Savchenko <oleksandr.savchenko.dn@bp.renesas.com>
---
 drivers/net/wireless/renesas/ra6w/dev.c | 233 ++++++++++++++++++++++++
 1 file changed, 233 insertions(+)
 create mode 100644 drivers/net/wireless/renesas/ra6w/dev.c
diff mbox series

Patch

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;
+}