// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
* All rights reserved.
*/
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include "cfg80211.h"
#include "wlan_cfg.h"
#define WILC_MULTICAST_TABLE_SIZE 8
static irqreturn_t isr_uh_routine(int irq, void *user_data)
{
struct net_device *dev = user_data;
struct wilc_vif *vif = netdev_priv(dev);
struct wilc *wilc = vif->wilc;
if (wilc->close) {
netdev_err(dev, "Can't handle UH interrupt\n");
return IRQ_HANDLED;
}
return IRQ_WAKE_THREAD;
}
static irqreturn_t isr_bh_routine(int irq, void *userdata)
{
struct net_device *dev = userdata;
struct wilc_vif *vif = netdev_priv(userdata);
struct wilc *wilc = vif->wilc;
if (wilc->close) {
netdev_err(dev, "Can't handle BH interrupt\n");
return IRQ_HANDLED;
}
wilc_handle_isr(wilc);
return IRQ_HANDLED;
}
static int init_irq(struct net_device *dev)
{
struct wilc_vif *vif = netdev_priv(dev);
struct wilc *wl = vif->wilc;
int ret;
ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine,
isr_bh_routine,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"WILC_IRQ", dev);
if (ret) {
netdev_err(dev, "Failed to request IRQ [%d]\n", ret);
return ret;
}
netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", wl->dev_irq_num);
return 0;
}
static void deinit_irq(struct net_device *dev)
{
struct wilc_vif *vif = netdev_priv(dev);
struct wilc *wilc = vif->wilc;
/* Deinitialize IRQ */
if (wilc->dev_irq_num)
free_irq(wilc->dev_irq_num, wilc);
}
void wilc_mac_indicate(struct wilc *wilc)
{
s8 status;
wilc_wlan_cfg_get_val(wilc, WID_STATUS, &status, 1);
if (wilc->mac_status == WILC_MAC_STATUS_INIT) {
wilc->mac_status = status;
complete(&wilc->sync_event);
} else {
wilc->mac_status = status;
}
}
static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
{
struct net_device *ndev = NULL;
struct wilc_vif *vif;
struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header;
list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
if (vif->mode == WILC_STATION_MODE)
if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) {
ndev = vif->ndev;
goto out;
}
if (vif->mode == WILC_AP_MODE)
if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) {
ndev = vif->ndev;
goto out;
}
}
out:
return ndev;
}
void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode)
{
struct wilc_vif *vif = netdev_priv(wilc_netdev);
if (bssid)
ether_addr_copy(vif->bssid, bssid);
else
eth_zero_addr(vif->bssid);
vif->mode = mode;
}
int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
{
int srcu_idx;
u8 ret_val = 0;
struct wilc_vif *vif;
srcu_idx = srcu_read_lock(&wilc->srcu);
list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
if (!is_zero_ether_addr(vif->bssid))
ret_val++;
}
srcu_read_unlock(&wilc->srcu, srcu_idx);
return ret_val;
}
static int wilc_txq_task(void *vp)
{
int ret;
u32 txq_count;
struct wilc *wl = vp;
complete(&wl->txq_thread_started);
while (1) {
wait_for_completion(&wl->txq_event);
if (wl->close) {
complete(&wl->txq_thread_started);
while (!kthread_should_stop())
schedule();
break;
}
do {
ret