// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/if_ether.h>
#include "core.h"
#include "bus.h"
#include "trans.h"
#include "commands.h"
#include "cfg80211.h"
#include "event.h"
#include "util.h"
#include "switchdev.h"
#define QTNF_DMP_MAX_LEN 48
#define QTNF_PRIMARY_VIF_IDX 0
static bool slave_radar = true;
module_param(slave_radar, bool, 0644);
MODULE_PARM_DESC(slave_radar, "set 0 to disable radar detection in slave mode");
static bool dfs_offload;
module_param(dfs_offload, bool, 0644);
MODULE_PARM_DESC(dfs_offload, "set 1 to enable DFS offload to firmware");
static struct dentry *qtnf_debugfs_dir;
bool qtnf_slave_radar_get(void)
{
return slave_radar;
}
bool qtnf_dfs_offload_get(void)
{
return dfs_offload;
}
struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid)
{
struct qtnf_wmac *mac = NULL;
if (unlikely(macid >= QTNF_MAX_MAC)) {
pr_err("invalid MAC index %u\n", macid);
return NULL;
}
mac = bus->mac[macid];
if (unlikely(!mac)) {
pr_err("MAC%u: not initialized\n", macid);
return NULL;
}
return mac;
}
/* Netdev handler for open.
*/
static int qtnf_netdev_open(struct net_device *ndev)
{
netif_carrier_off(ndev);
qtnf_netdev_updown(ndev, 1);
return 0;
}
/* Netdev handler for close.
*/
static int qtnf_netdev_close(struct net_device *ndev)
{
netif_carrier_off(ndev);
qtnf_virtual_intf_cleanup(ndev);
qtnf_netdev_updown(ndev, 0);
return 0;
}
static void qtnf_packet_send_hi_pri(struct sk_buff *skb)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(skb->dev);
skb_queue_tail(&vif->high_pri_tx_queue, skb);
queue_work(vif->mac->bus->hprio_workqueue, &vif->high_pri_tx_work);
}
/* Netdev handler for data transmission.
*/
static netdev_tx_t
qtnf_netdev_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct qtnf_vif *vif;
struct qtnf_wmac *mac;
vif = qtnf_netdev_get_priv(ndev);
if (unlikely(skb->dev != ndev)) {
pr_err_ratelimited("invalid skb->dev");
dev_kfree_skb_any(skb);
return 0;
}
if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)) {
pr_err_ratelimited("%s: VIF not initialized\n", ndev->name);
dev_kfree_skb_any(skb);
return 0;
}
mac = vif->mac;
if (unlikely(!mac)) {
pr_err_ratelimited("%s: NULL mac pointer", ndev->name);
dev_kfree_skb_any(skb);
return 0;
}
if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
pr_err_ratelimited("%s: invalid skb len %d\n", ndev->name,
skb->len);
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
return 0;
}
/* tx path is enabled: reset vif timeout */
vif->cons_tx_timeout_cnt = 0;
if (unlikely(skb->protocol == htons(ETH_P_PAE))) {
qtnf_packet_send_hi_pri(skb);
qtnf_update_tx_stats(ndev, skb);
return NETDEV_TX_OK;
}
return qtnf_bus_data_tx(mac->bus, skb, mac->macid, vif->vifid);
}
/* Netdev handler for getting stats.
*/
static void qtnf_netdev_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
unsigned int start;
int cpu;
netdev_stats_to_stats64(stats, &ndev->stats);
if (!vif->stats64)
return;
for_each_possible_cpu(cpu) {
struct pcpu_sw_netstats *stats64;
u64 rx_packets, rx_bytes;
u64 tx_packets, tx_bytes;
stats64 = per_cpu_ptr(vif->stats64, cpu);
do {
start = u64_stats_fetch_begin_irq(&stats64->syncp);
rx_packets = stats64->rx_packets;
rx_bytes =