blob: b628e530c3d262ea9ab8046f752e559a888b06a4 [file] [log] [blame]
/**
Copyright (c) 2008 - 2013 Quantenna Communications Inc
All Rights Reserved
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/netdevice.h>
#include <linux/igmp.h>
#include <net/iw_handler.h> /* wireless_send_event(..) */
#include <net/sch_generic.h>
#include <asm/hardware.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include <linux/net/bridge/br_public.h>
#include <linux/gpio.h>
#else
#include <asm/gpio.h>
#endif
#include <qtn/qdrv_sch.h>
#include "qdrv_features.h"
#include "qdrv_debug.h"
#include "qdrv_mac.h"
#include "qdrv_soc.h"
#include "qdrv_comm.h"
#include "qtn/qdrv_bld.h"
#include "qdrv_wlan.h"
#include "qdrv_hal.h"
#include "qdrv_vap.h" /* For vnet_send_ioctl() etc ... */
#include "qdrv_control.h"
#include "qdrv_txbf.h"
#include "qdrv_radar.h"
#include "qdrv_pktlogger.h"
#include "qdrv_config.h"
#include "qdrv_pcap.h"
#include "qdrv_auc.h"
#include "qdrv_mac_reserve.h"
#include "qdrv_netdebug_checksum.h"
#include <qtn/qtn_buffers.h>
#include <qtn/qtn_global.h>
#include <qtn/registers.h> /* To get to mac->reg-> .... */
#include <qtn/muc_phy_stats.h> /* To get to qtn_stats_log .... */
#include <qtn/shared_params.h>
#include <qtn/hardware_revision.h>
#include <qtn/bootcfg.h>
#include <qtn/qtn_trace.h>
#include <qtn/qtn_global.h>
#include "qdrv_muc_stats.h"
#include "qdrv_sch_const.h"
#include "qdrv_sch_wmm.h"
#include "net80211/ieee80211_beacon_desc.h"
#ifdef CONFIG_QVSP
#include "qtn/qvsp.h"
#endif
#include <radar/radar.h>
#include <qtn/muc_phy_stats.h>
#include <linux/file.h>
#include <linux/syscalls.h>
#include <linux/ctype.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include <linux/pm_qos.h>
#else
#include <linux/pm_qos_params.h>
#endif
#include <common/ruby_pm.h>
#include <ruby/gpio.h>
#include <ruby/pm.h>
#include <ruby/plat_dma_addr.h>
#include <asm/board/board_config.h>
#include <asm/board/troubleshoot.h>
#include <asm/cacheflush.h>
#include <qtn/topaz_hbm.h>
#include <qtn/topaz_fwt_sw.h>
#include <qtn/topaz_congest_queue.h>
#include "soc.h"
#include "qtn_logging.h"
#include <net/arp.h>
#ifdef CONFIG_IPV6
#include <net/ip6_checksum.h>
#endif
/* Delay prior to enabling hang detection */
#define QDRV_WLAN_HR_DELAY_SECS 3
#define QDRV_DFLT_MIN_TXPOW ((int8_t) -20)
#define QDRV_DFLT_MAX_TXPOW 19
#define QDRV_WLAN_IGMP_QUERY_INTERVAL 125
#define NET_IP_ALIGN 2
#define SE95_DEVICE_ADDR 0x49
#define RSSI_OFFSET_FROM_10THS_DBM 900
u_int8_t g_bb_enabled = 0;
extern uint32_t g_carrier_id;
extern __sram_data const int qdrv_sch_band_prio[5];
extern struct qdrv_sch_band_aifsn qdrv_sch_band_chg_prio[5];
#ifdef CONFIG_QVSP
static void qdrv_wlan_manual_ba_throt(struct qdrv_wlan *qw, struct qdrv_vap *qv, unsigned int value);
static void qdrv_wlan_manual_wme_throt(struct qdrv_wlan *qw, struct qdrv_vap *qv, unsigned int value);
#endif
int g_qdrv_non_qtn_assoc = 0;
void enable_bb(int index, u32 channel);
void bb_rf_drv_set_channel(u32 bb_index, u32 freq_band, u32 channel);
struct timer_list qdrv_wps_button_timer;
int g_triggers_on = 0;
static void qdrv_wlan_set_11g_erp(struct ieee80211vap *vap, int on);
static struct qtn_rateentry rate_table_11a[] =
{
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{12, 60, 0, 0, 1, QTN_RATE_PHY_OFDM},
{18, 90, 0, 0, 0, QTN_RATE_PHY_OFDM},
{24, 120, 2, 0, 1, QTN_RATE_PHY_OFDM},
{36, 180, 2, 0, 0, QTN_RATE_PHY_OFDM},
{48, 240, 4, 0, 0, QTN_RATE_PHY_OFDM},
{72, 360, 4, 0, 0, QTN_RATE_PHY_OFDM},
{96, 480, 4, 0, 0, QTN_RATE_PHY_OFDM},
{108, 540, 4, 0, 0, QTN_RATE_PHY_OFDM},
};
static struct qtn_rateentry rate_table_11b[] =
{
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{2, 10, 0, 0, 1, QTN_RATE_PHY_CCK},
{4, 20, 1, 1, 1, QTN_RATE_PHY_CCK},
{11, 55, 1, 1, 0, QTN_RATE_PHY_CCK},
{22, 110, 1, 1, 0, QTN_RATE_PHY_CCK},
};
static struct qtn_rateentry rate_table_11g[] =
{
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{2, 10, 0, 0, 1, QTN_RATE_PHY_CCK},
{4, 20, 1, 1, 1, QTN_RATE_PHY_CCK},
{11, 55, 2, 1, 1, QTN_RATE_PHY_CCK},
{22, 110, 3, 1, 1, QTN_RATE_PHY_CCK},
{12, 60, 4, 0, 1, QTN_RATE_PHY_OFDM},
{18, 90, 4, 0, 0, QTN_RATE_PHY_OFDM},
{24, 120, 6, 0, 1, QTN_RATE_PHY_OFDM},
{36, 180, 6, 0, 0, QTN_RATE_PHY_OFDM},
{48, 240, 8, 0, 1, QTN_RATE_PHY_OFDM},
{72, 360, 8, 0, 0, QTN_RATE_PHY_OFDM},
{96, 480, 8, 0, 0, QTN_RATE_PHY_OFDM},
{108, 540, 8, 0, 0, QTN_RATE_PHY_OFDM},
};
struct qtn_rateentry rate_table_11na[] = {
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{12, 60, 0, 0, 1, QTN_RATE_PHY_OFDM},
{18, 90, 0, 0, 0, QTN_RATE_PHY_OFDM},
{24, 120, 2, 0, 1, QTN_RATE_PHY_OFDM},
{36, 180, 2, 0, 0, QTN_RATE_PHY_OFDM},
{48, 240, 4, 0, 0, QTN_RATE_PHY_OFDM},
{72, 360, 4, 0, 0, QTN_RATE_PHY_OFDM},
{96, 480, 4, 0, 0, QTN_RATE_PHY_OFDM},
{108, 540, 4, 0, 0, QTN_RATE_PHY_OFDM},
{QTN_RATE_11N | 0, 65, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 1, 130, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 2, 195, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 3, 260, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 4, 390, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 5, 520, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 6, 585, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 7, 650, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 8, 130, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 9, 260, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 10, 390, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 11, 520, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 12, 780, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 13, 1040, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 14, 1170, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 15, 1300, 4, 0, 0, QTN_RATE_PHY_HT},
};
/*
* Only legacy rate are been added to it. Other rate flags will be updated
* when we MCS changes are made
*/
struct qtn_rateentry rate_table_11ac[] = {
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{12, 60, 0, 0, 1, QTN_RATE_PHY_OFDM},
{18, 90, 0, 0, 0, QTN_RATE_PHY_OFDM},
{24, 120, 2, 0, 1, QTN_RATE_PHY_OFDM},
{36, 180, 2, 0, 0, QTN_RATE_PHY_OFDM},
{48, 240, 4, 0, 0, QTN_RATE_PHY_OFDM},
{72, 360, 4, 0, 0, QTN_RATE_PHY_OFDM},
{96, 480, 4, 0, 0, QTN_RATE_PHY_OFDM},
{108, 540, 4, 0, 0, QTN_RATE_PHY_OFDM},
};
static struct qtn_rateentry rate_table_11ng[] =
{
/* ieee rate ctl short basic phy */
/* rate 100kbps indx pre rate type */
{2, 10, 0, 0, 1, QTN_RATE_PHY_CCK},
{4, 20, 1, 1, 1, QTN_RATE_PHY_CCK},
{11, 55, 2, 1, 1, QTN_RATE_PHY_CCK},
{22, 110, 3, 1, 1, QTN_RATE_PHY_CCK},
{12, 60, 0, 0, 1, QTN_RATE_PHY_OFDM},
{18, 90, 0, 0, 0, QTN_RATE_PHY_OFDM},
{24, 120, 2, 0, 1, QTN_RATE_PHY_OFDM},
{36, 180, 2, 0, 0, QTN_RATE_PHY_OFDM},
{48, 240, 4, 0, 1, QTN_RATE_PHY_OFDM},
{72, 360, 4, 0, 0, QTN_RATE_PHY_OFDM},
{96, 480, 4, 0, 0, QTN_RATE_PHY_OFDM},
{108, 540, 4, 0, 0, QTN_RATE_PHY_OFDM},
{QTN_RATE_11N | 0, 65, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 1, 130, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 2, 195, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 3, 260, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 4, 390, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 5, 520, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 6, 585, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 7, 650, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 8, 130, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 9, 260, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 10, 390, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 11, 520, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 12, 780, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 13, 1040, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 14, 1170, 4, 0, 0, QTN_RATE_PHY_HT},
{QTN_RATE_11N | 15, 1300, 4, 0, 0, QTN_RATE_PHY_HT},
};
static struct qtn_channel qtn_channels_2ghz[] =
{
/* FIXME: Not assigning the correct pri chan for 2G mode */
/* channel number frequency 40M upper / lower*/
{1, 2412, IEEE80211_CHAN_HT40U, 3, 0, 0, 0},
{2, 2417, IEEE80211_CHAN_HT40U, 4, 0, 0, 0},
{3, 2422, IEEE80211_CHAN_HT40U, 5, 0, 0, 0},
{4, 2427, IEEE80211_CHAN_HT40U, 6, 0, 0, 0},
{5, 2432, IEEE80211_CHAN_HT40U, 7, 0, 0, 0},
{6, 2437, IEEE80211_CHAN_HT40U, 8, 0, 0, 0},
{7, 2442, IEEE80211_CHAN_HT40U, 9, 0, 0, 0},
{8, 2447, IEEE80211_CHAN_HT40D, 6, 0, 0, 0},
{9, 2452, IEEE80211_CHAN_HT40D, 7, 0, 0, 0},
{10, 2457, IEEE80211_CHAN_HT40D, 8, 0, 0, 0},
{11, 2462, IEEE80211_CHAN_HT40D, 9, 0, 0, 0},
{12, 2467, IEEE80211_CHAN_HT40D, 10, 0, 0, 0},
{13, 2472, IEEE80211_CHAN_HT40D, 11, 0, 0, 0},
};
static struct qtn_channel qtn_channels_5ghz[] =
{
/* channel number frequency 40MHz mode CFreq40 CFreq80 Cfreq160 80MHZ mode*/
{36, 5180, IEEE80211_CHAN_HT40U, 38, 42, 50, IEEE80211_CHAN_VHT80_LL},
{40, 5200, IEEE80211_CHAN_HT40D, 38, 42, 50, IEEE80211_CHAN_VHT80_LU},
{44, 5220, IEEE80211_CHAN_HT40U, 46, 42, 50, IEEE80211_CHAN_VHT80_UL},
{48, 5240, IEEE80211_CHAN_HT40D, 46, 42, 50, IEEE80211_CHAN_VHT80_UU},
{52, 5260, IEEE80211_CHAN_HT40U, 54, 58, 50, IEEE80211_CHAN_VHT80_LL},
{56, 5280, IEEE80211_CHAN_HT40D, 54, 58, 50, IEEE80211_CHAN_VHT80_LU},
{60, 5300, IEEE80211_CHAN_HT40U, 62, 58, 50, IEEE80211_CHAN_VHT80_UL},
{64, 5320, IEEE80211_CHAN_HT40D, 62, 58, 50, IEEE80211_CHAN_VHT80_UU},
{100, 5500, IEEE80211_CHAN_HT40U, 102, 106, 114, IEEE80211_CHAN_VHT80_LL},
{104, 5520, IEEE80211_CHAN_HT40D, 102, 106, 114, IEEE80211_CHAN_VHT80_LU},
{108, 5540, IEEE80211_CHAN_HT40U, 110, 106, 114, IEEE80211_CHAN_VHT80_UL},
{112, 5560, IEEE80211_CHAN_HT40D, 110, 106, 114, IEEE80211_CHAN_VHT80_UU},
{116, 5580, IEEE80211_CHAN_HT40U, 118, 122, 114, IEEE80211_CHAN_VHT80_LL},
{120, 5600, IEEE80211_CHAN_HT40D, 118, 122, 114, IEEE80211_CHAN_VHT80_LU},
{124, 5620, IEEE80211_CHAN_HT40U, 126, 122, 114, IEEE80211_CHAN_VHT80_UL},
{128, 5640, IEEE80211_CHAN_HT40D, 126, 122, 114, IEEE80211_CHAN_VHT80_UU},
{132, 5660, IEEE80211_CHAN_HT40U, 134, 138, 0, IEEE80211_CHAN_VHT80_LL},
{136, 5680, IEEE80211_CHAN_HT40D, 134, 138, 0, IEEE80211_CHAN_VHT80_LU},
{140, 5700, IEEE80211_CHAN_HT40U, 142, 138, 0, IEEE80211_CHAN_VHT80_UL},
{144, 5720, IEEE80211_CHAN_HT40D, 142, 138, 0, IEEE80211_CHAN_VHT80_UU},
{149, 5745, IEEE80211_CHAN_HT40U, 151, 155, 0, IEEE80211_CHAN_VHT80_LL},
{153, 5765, IEEE80211_CHAN_HT40D, 151, 155, 0, IEEE80211_CHAN_VHT80_LU},
{157, 5785, IEEE80211_CHAN_HT40U, 159, 155, 0, IEEE80211_CHAN_VHT80_UL},
{161, 5805, IEEE80211_CHAN_HT40D, 159, 155, 0, IEEE80211_CHAN_VHT80_UU},
{165, 5825, IEEE80211_CHAN_HT40U, 0, 0, 0, 0},
{169, 5845, IEEE80211_CHAN_HT40D, 0, 0, 0, 0},
{184, 4920, IEEE80211_CHAN_HT40U, 0, 190, 0, IEEE80211_CHAN_VHT80_LL},
{188, 4940, IEEE80211_CHAN_HT40D, 0, 190, 0, IEEE80211_CHAN_VHT80_LU},
{192, 4960, IEEE80211_CHAN_HT40U, 0, 190, 0, IEEE80211_CHAN_VHT80_UL},
{196, 4980, IEEE80211_CHAN_HT40D, 0, 190, 0, IEEE80211_CHAN_VHT80_UU},
};
static void set_channels(struct ieee80211com *ic, int nchans,
struct ieee80211_channel *inchans)
{
int i;
if (nchans > IEEE80211_CHAN_MAX + 1) {
nchans = IEEE80211_CHAN_MAX + 1;
}
ic->ic_nchans = nchans;
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
for(i = 0; i < nchans; i++) {
ic->ic_channels[i] = inchans[i];
/* make sure only valid 2.4G or 5G channels are set as available */
if (((inchans[i].ic_ieee >= QTN_2G_FIRST_OPERATING_CHAN) && (inchans[i].ic_ieee <= QTN_2G_LAST_OPERATING_CHAN)) ||
((inchans[i].ic_ieee >= QTN_5G_FIRST_OPERATING_CHAN) && (inchans[i].ic_ieee <= QTN_5G_LAST_OPERATING_CHAN))) {
setbit(ic->ic_chan_avail, inchans[i].ic_ieee);
if (IEEE80211_IS_CHAN_HT40(&inchans[i])) {
setbit(ic->ic_chan_active_40, inchans[i].ic_ieee);
}
if (IEEE80211_IS_CHAN_VHT80(&inchans[i])) {
setbit(ic->ic_chan_active_80, inchans[i].ic_ieee);
}
setbit(ic->ic_chan_active_20, inchans[i].ic_ieee);
}
}
memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail));
}
static int set_rates(struct qdrv_wlan *qw, enum ieee80211_phymode mode)
{
struct ieee80211com *ic = &qw->ic;
struct ieee80211_rateset *rs;
struct qtn_ratetable *rt;
int maxrates, i;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
#define N(a) (sizeof(a)/sizeof(a[0]))
switch (mode) {
case IEEE80211_MODE_11A:
qw->qw_rates[mode].rt_entries = rate_table_11a;
qw->qw_rates[mode].rt_num = N(rate_table_11a);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11a);
break;
case IEEE80211_MODE_11B:
qw->qw_rates[mode].rt_entries = rate_table_11b;
qw->qw_rates[mode].rt_num = N(rate_table_11b);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11b);
break;
case IEEE80211_MODE_11G:
qw->qw_rates[mode].rt_entries = rate_table_11g;
qw->qw_rates[mode].rt_num = N(rate_table_11g);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11g);
break;
case IEEE80211_MODE_11NA:
case IEEE80211_MODE_11NA_HT40PM:
qw->qw_rates[mode].rt_entries = rate_table_11na;
qw->qw_rates[mode].rt_num = N(rate_table_11na);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11a);
break;
case IEEE80211_MODE_11NG:
case IEEE80211_MODE_11NG_HT40PM:
qw->qw_rates[mode].rt_entries = rate_table_11ng;
qw->qw_rates[mode].rt_num = N(rate_table_11ng);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11g);
break;
case IEEE80211_MODE_11AC_VHT20PM:
case IEEE80211_MODE_11AC_VHT40PM:
case IEEE80211_MODE_11AC_VHT80PM:
qw->qw_rates[mode].rt_entries = rate_table_11ac;
qw->qw_rates[mode].rt_num = N(rate_table_11ac);
qw->qw_rates[mode].rt_legacy_num = N(rate_table_11a);
break;
default:
DBGPRINTF_E("mode unknown\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -EINVAL;
}
#undef N
if ((rt = &qw->qw_rates[mode]) == NULL) {
DBGPRINTF_E("rt NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -EINVAL;
}
if (rt->rt_num > IEEE80211_RATE_MAXSIZE) {
DBGPRINTF_E("Rate table is too small (%u > %u)\n",
rt->rt_num, IEEE80211_RATE_MAXSIZE);
maxrates = IEEE80211_RATE_MAXSIZE;
} else {
maxrates = rt->rt_num;
}
rs = &ic->ic_sup_rates[mode];
memset(rs, 0, sizeof(struct ieee80211_rateset));
for (i = 0; i < maxrates; i++) {
rs->rs_rates[i] = (rt->rt_entries[i].re_basicrate) ?
(rt->rt_entries[i].re_ieeerate | IEEE80211_RATE_BASIC) :
rt->rt_entries[i].re_ieeerate;
}
rs->rs_legacy_nrates = rt->rt_legacy_num;
rs->rs_nrates = maxrates;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
static int set_mode(struct qdrv_wlan *qw, enum ieee80211_phymode mode)
{
qw->qw_currt = &qw->qw_rates[mode];
qw->qw_curmode = mode;
#if 0
qw->qw_minrateix = 0;
#endif
return 0;
}
static void qdrv_wlan_tx_sch_node_info(void *s, struct ieee80211_node *ni)
{
const struct Qdisc *sch = netdev_get_tx_queue(ni->ni_vap->iv_dev, 0)->qdisc;
struct seq_file *sq = (struct seq_file *)s;
const struct qdrv_sch_node_data *nd = &ni->ni_tx_sch;
int i;
static const char *band_id[] = {"BE", "BK", "VI", "VO", "CT"};
if (!sq) {
return;
}
seq_printf(sq, "%s AID=%u ref=%u qdisc=%p tokens=%u muc=%d over_thresh=%u/%u low_rate=%u\n",
ether_sprintf(ni->ni_macaddr),
IEEE80211_AID(ni->ni_associd),
ieee80211_node_refcnt(ni),
sch,
ni->ni_tx_sch.used_tokens, /* enqueued or sent to MuC */
ni->ni_tx_sch.muc_queued, /* dequeued, sent to MuC, not yet tx done */
nd->over_thresh,
nd->over_thresh_cnt,
nd->low_rate);
seq_printf(sq, " Queue Depth Sent Dropped Victim Active\n");
for (i = 0; i < ARRAY_SIZE(nd->bands); i++) {
const struct qdrv_sch_node_band_data *nbd;
nbd = &nd->bands[i];
seq_printf(sq, " %i-%s %-5i %-10u %-10u %-10u %u\n",
i,
band_id[i],
skb_queue_len(&nbd->queue),
nbd->sent,
nbd->dropped,
nbd->dropped_victim,
qdrv_sch_node_is_active(nbd, nd, i));
}
}
static void qdrv_wlan_tx_sch_init(struct qdrv_wlan *qw)
{
struct qdrv_sch_shared_data *sd;
sd = qdrv_sch_shared_data_init(QTN_BUFS_WMAC_TX_QDISC, QDRV_TX_SCH_RED_MASK);
if (sd == NULL) {
panic("%s: could not allocate tx_sch shared data\n", __FUNCTION__);
}
qw->tx_sch_shared_data = sd;
}
/*
* Set a per-node threshold for packets queued to the MuC, based on the number of associated nodes,
* including WDS nodes.
* PERNODE_TBD - all WDS nodes should count as one under the current scheme
*/
static void qdrv_wlan_muc_node_thresh_set(struct qdrv_wlan *qw, struct ieee80211com *ic,
uint8_t assoc_cnt)
{
if (assoc_cnt == 0) {
assoc_cnt = 1;
}
qw->tx_if.muc_thresh_high = MAX(
(qw->tx_if.list_max_size / assoc_cnt), QDRV_TXDESC_THRESH_MAX_MIN);
qw->tx_if.muc_thresh_low = qw->tx_if.muc_thresh_high - QDRV_TXDESC_THRESH_MIN_DIFF;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"per-node thresholds changed - high=%u low=%u nodes=%u\n",
qw->tx_if.muc_thresh_high, qw->tx_if.muc_thresh_low, ic->ic_sta_assoc);
}
extern int g_wlan_tot_node_alloc;
extern int g_wlan_tot_node_alloc_tmp;
extern int g_wlan_tot_node_free;
extern int g_wlan_tot_node_free_tmp;
static struct ieee80211_node *qdrv_node_alloc(struct ieee80211_node_table *nt,
struct ieee80211vap *vap, const uint8_t *macaddr, const uint8_t tmp_node)
{
struct qdrv_node *qn;
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct net_device *vdev = vap->iv_dev;
struct qtn_node_shared_stats_list *shared_stats;
unsigned long flags;
qn = kmalloc(sizeof(struct qdrv_node), GFP_ATOMIC);
if (qn == NULL) {
DBGPRINTF_E("kmalloc failed\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return NULL;
}
memset(qn, 0, sizeof(struct qdrv_node));
dev_hold(vdev); /* Increase the reference count of the VAP netdev */
ic->ic_node_count++;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
DBGPRINTF(DBG_LL_INFO, QDRV_LF_TRACE | QDRV_LF_WLAN,
"Allocated node %p (total %d/%d)\n",
qn, ic->ic_node_count, netdev_refcnt_read(vdev));
#else
DBGPRINTF(DBG_LL_INFO, QDRV_LF_TRACE | QDRV_LF_WLAN,
"Allocated node %p (total %d/%d)\n",
qn, ic->ic_node_count, atomic_read(&vdev->refcnt));
#endif
qn->qn_node.ni_vap = vap;
TAILQ_INSERT_TAIL(&qv->allnodes, qn, qn_next);
qdrv_tx_sch_node_data_init(qdrv_tx_sch_vap_get_qdisc(vdev), qw->tx_sch_shared_data,
&qn->qn_node.ni_tx_sch, ic->ic_sta_assoc + 1);
if (!tmp_node) {
local_irq_save(flags);
shared_stats = TAILQ_FIRST(&qw->shared_pernode_stats_head);
if (shared_stats == NULL) {
DBGPRINTF_E("Failed to obtain shared_stats for new node\n");
local_irq_restore(flags);
dev_put(vdev);
ic->ic_node_count--;
kfree(qn);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return NULL;
}
TAILQ_REMOVE(&qw->shared_pernode_stats_head, shared_stats, next);
local_irq_restore(flags);
memset(shared_stats, 0, sizeof(*shared_stats));
qn->qn_node.ni_shared_stats = (struct qtn_node_shared_stats *) shared_stats;
qn->qn_node.ni_shared_stats_phys = (void*)((unsigned long)shared_stats -
(unsigned long)qw->shared_pernode_stats_pool +
(unsigned long)qw->shared_pernode_stats_phys);
#ifdef CONFIG_QVSP
qvsp_node_init(&qn->qn_node);
#endif
g_wlan_tot_node_alloc++;
qdrv_wlan_muc_node_thresh_set(qw, ic, ic->ic_sta_assoc + 1);
} else {
g_wlan_tot_node_alloc_tmp++;
}
return &qn->qn_node;
}
static void qdrv_node_free(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_node *qn = container_of(ni, struct qdrv_node, qn_node);
struct ieee80211vap *vap = ni->ni_vap;
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct net_device *vdev = vap->iv_dev;
unsigned long flags;
if (ni->ni_shared_stats) {
local_irq_save(flags);
TAILQ_INSERT_TAIL(&qw->shared_pernode_stats_head,
(struct qtn_node_shared_stats_list *) ni->ni_shared_stats, next);
local_irq_restore(flags);
g_wlan_tot_node_free++;
qdrv_wlan_muc_node_thresh_set(qw, ic, ic->ic_sta_assoc);
} else {
g_wlan_tot_node_free_tmp++;
}
qdrv_tx_sch_node_data_exit(&ni->ni_tx_sch, ic->ic_sta_assoc);
TAILQ_REMOVE(&qv->allnodes, qn, qn_next);
dev_put(vdev);
}
static u_int32_t qdrv_set_channel_setup(const struct ieee80211com *ic,
const struct ieee80211_channel *chan)
{
u_int32_t ieee_chan;
uint32_t qtn_chan = 0;
int32_t pwr;
int force_bw_20 = 0;
int force_bw_40 = 0;
int max_bw = BW_HT80;
int tdls_offchan = !!(chan->ic_ext_flags & IEEE80211_CHAN_TDLS_OFF_CHAN);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ieee_chan = (u_int32_t)chan->ic_ieee;
pwr = chan->ic_maxpower;
force_bw_20 = (ic->ic_flags_ext & IEEE80211_FEXT_SCAN_20) &&
((ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) || tdls_offchan);
force_bw_40 = ic->ic_flags_ext & IEEE80211_FEXT_SCAN_40;
if ((ic->ic_opmode == IEEE80211_M_STA) && ic->ic_bss_bw && !tdls_offchan) {
max_bw = ic->ic_bss_bw;
}
qtn_chan = SM(ieee_chan, QTN_CHAN_IEEE);
if (!force_bw_20 && ((ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) || tdls_offchan)) {
if ((chan->ic_flags & IEEE80211_CHAN_HT40D) && (max_bw >= BW_HT40)) {
qtn_chan |= QTN_CHAN_FLG_PRI_HI | QTN_CHAN_FLG_HT40;
} else if ((chan->ic_flags & IEEE80211_CHAN_HT40U) && (max_bw >= BW_HT40)) {
qtn_chan |= QTN_CHAN_FLG_HT40;
}
if (!force_bw_40 && IS_IEEE80211_VHT_ENABLED(ic) &&
IEEE80211_IS_VHT_80(ic) &&
(chan->ic_flags & IEEE80211_CHAN_VHT80) &&
(max_bw >= BW_HT80)) {
qtn_chan |= QTN_CHAN_FLG_VHT80;
}
}
qtn_chan |= SM(pwr, QTN_CHAN_PWR);
if (chan->ic_flags & IEEE80211_CHAN_DFS) {
qtn_chan |= QTN_CHAN_FLG_DFS;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"Hlink setting channel %08X Chan %d Pwr %d\n",
qtn_chan, ieee_chan, pwr);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qtn_chan;
}
static uint32_t qdrv_set_channel_freqband_setup(const struct ieee80211com *ic, const struct ieee80211_channel *chan)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
uint32_t freqband = 0;
freqband |= SM(qw->rf_chipid, QTN_BAND_FREQ);
return freqband;
}
static int qdrv_wlan_80211_get_cap_bw(struct ieee80211com *ic)
{
int bw;
switch (ic->ic_phymode) {
case IEEE80211_MODE_11A:
case IEEE80211_MODE_11B:
case IEEE80211_MODE_11G:
case IEEE80211_MODE_11NA:
case IEEE80211_MODE_11NG:
case IEEE80211_MODE_11AC_VHT20PM:
bw = BW_HT20;
break;
case IEEE80211_MODE_11NA_HT40PM:
case IEEE80211_MODE_11NG_HT40PM:
case IEEE80211_MODE_11AC_VHT40PM:
bw = BW_HT40;
break;
case IEEE80211_MODE_11AC_VHT80PM:
bw = BW_HT80;
break;
case IEEE80211_MODE_11AC_VHT160PM:
bw = BW_HT160;
break;
default:
bw = BW_INVALID;
break;
}
return bw;
}
static int qdrv_chan_has_radar(struct ieee80211com *ic, struct ieee80211_channel *base_chan,
struct ieee80211_channel *chan, int is_req_chan)
{
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"%s: check chan=%u (%u MHz)\n",
__func__, chan->ic_ieee, chan->ic_freq);
if ((chan < ic->ic_channels) || (chan > (ic->ic_channels + ic->ic_nchans))) {
DBGPRINTF_E("%schannel %u (%u MHz) is invalid\n",
chan == base_chan ? "" : "secondary ",
chan->ic_ieee, chan->ic_freq);
return 1;
}
if (chan->ic_flags & IEEE80211_CHAN_RADAR) {
if (is_req_chan) {
if (chan == base_chan)
printk("selected channel %u cannot be used - has radar\n",
base_chan->ic_ieee);
else
printk("selected channel %u cannot be used - "
"secondary channel %u has radar\n",
base_chan->ic_ieee, chan->ic_ieee);
}
return 1;
}
return 0;
}
static int qdrv_check_channel(struct ieee80211com *ic, struct ieee80211_channel *chan,
int fast_switch, int is_req_chan)
{
uint16_t band_flags;
struct ieee80211_channel *low_chan = NULL;
int check_curchan = !(ic->ic_flags & IEEE80211_F_SCAN);
int bw = qdrv_wlan_80211_get_cap_bw(ic);
if (ic->ic_curchan == NULL) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - ic_curchan NULL\n", __func__);
return 0;
}
if (chan == NULL || chan == IEEE80211_CHAN_ANYC) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - channel invalid\n", __func__);
return 0;
}
band_flags = ic->ic_curchan->ic_flags &
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
if (fast_switch && (!(ieee80211_is_chan_available(chan)) ||
isset(ic->ic_chan_pri_inactive, chan->ic_ieee))) {
return 0;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"%s: chan=%u isset=%u bw=%u flags=0x%08x/0x%04x cur=%u set=%u\n",
__func__,
chan->ic_ieee, !!isset(ic->ic_chan_active, chan->ic_ieee), bw,
chan->ic_flags, band_flags, ic->ic_curchan->ic_ieee, ic->ic_chan_is_set);
if (check_curchan && (chan->ic_freq == ic->ic_curchan->ic_freq) && ic->ic_chan_is_set) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - current\n", __func__);
return 0;
};
if (!isset(ic->ic_chan_active, chan->ic_ieee)) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - inactive\n", __func__);
return 0;
};
if (!(chan->ic_flags & band_flags)) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - off band\n", __func__);
return 0;
};
/* ignore channels that do not match the required bandwidth */
if (IEEE80211_IS_VHT_80(ic)) {
if (!(chan->ic_flags & IEEE80211_CHAN_VHT80)) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: rejected - bw\n", __func__);
return 0;
}
}
/* ignore channels where radar has been detected */
switch (bw) {
case BW_HT160:
case BW_INVALID:
DBGPRINTF_E("invalid phy mode %u (needs adding to qdrv_wlan_80211_get_cap_bw?)\n",
ic->ic_phymode);
return 0;
case BW_HT80:
if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) {
low_chan = chan;
} else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) {
low_chan = chan - 1;
} else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) {
low_chan = chan - 2;
} else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) {
low_chan = chan - 3;
}
if (low_chan == NULL) {
DBGPRINTF_E("invalid ext flags %08x\n", chan->ic_ext_flags);
return 0;
}
if (qdrv_chan_has_radar(ic, chan, low_chan, is_req_chan) ||
qdrv_chan_has_radar(ic, chan, low_chan + 1, is_req_chan) ||
qdrv_chan_has_radar(ic, chan, low_chan + 2, is_req_chan) ||
qdrv_chan_has_radar(ic, chan, low_chan + 3, is_req_chan)) {
return 0;
}
break;
case BW_HT40:
if (chan->ic_flags & IEEE80211_CHAN_HT40D) {
low_chan = chan - 1;
} else {
low_chan = chan;
}
if (qdrv_chan_has_radar(ic, chan, low_chan, is_req_chan) ||
qdrv_chan_has_radar(ic, chan, low_chan + 1, is_req_chan)) {
return 0;
}
break;
case BW_HT20:
if (qdrv_chan_has_radar(ic, chan, chan, is_req_chan)) {
return 0;
}
break;
}
ic->ic_chan_is_set = 1;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"%s: channel %u (%u MHz) selected\n",
__func__, chan->ic_ieee, chan->ic_freq);
return 1;
}
static void qdrv_chan_occupy_record_finish(struct ieee80211com *ic, uint8_t new_chan)
{
struct ieee80211_chan_occupy_record *occupy_record = &ic->ic_chan_occupy_record;
uint8_t cur_chan = occupy_record->cur_chan;
if ((ic->ic_flags & IEEE80211_F_SCAN) &&
(ic->ic_scan->ss_flags & IEEE80211_SCAN_NOPICK)) {
return;
}
if (cur_chan && (new_chan != cur_chan)) {
occupy_record->cur_chan = 0;
occupy_record->prev_chan = cur_chan;
occupy_record->duration[cur_chan] += (jiffies - INITIAL_JIFFIES) / HZ -
occupy_record->occupy_start;
}
}
static void qdrv_chan_occupy_record_start(struct ieee80211com *ic, uint8_t new_chan)
{
struct ieee80211_chan_occupy_record *occupy_record = &ic->ic_chan_occupy_record;
if (occupy_record->cur_chan == 0) {
occupy_record->cur_chan = new_chan;
if (occupy_record->prev_chan != new_chan) {
occupy_record->times[new_chan]++;
}
occupy_record->occupy_start = (jiffies - INITIAL_JIFFIES) / HZ;
}
}
static bool qdrv_chan_compare_equality(struct ieee80211com *ic,
struct ieee80211_channel *prev_chan, struct ieee80211_channel *new_chan)
{
int bw = qdrv_wlan_80211_get_cap_bw(ic);
struct ieee80211_channel *low_chan = NULL;
int ret = false;
if ((!prev_chan) || (!new_chan)) {
return ret;
}
switch (bw) {
case BW_HT80:
if (prev_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) {
low_chan = prev_chan;
} else if (prev_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) {
low_chan = prev_chan - 1;
} else if (prev_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) {
low_chan = prev_chan - 2;
} else if (prev_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) {
low_chan = prev_chan - 3;
}
if ((low_chan == new_chan) || ((low_chan + 1) == new_chan) ||
((low_chan + 2) == new_chan) || ((low_chan + 3) == new_chan)) {
ret = true;
}
break;
case BW_HT40:
if (prev_chan->ic_flags & IEEE80211_CHAN_HT40D) {
low_chan = prev_chan - 1;
} else {
low_chan = prev_chan;
}
if ((low_chan == new_chan) || ((low_chan + 1) == new_chan)) {
ret = true;
}
break;
case BW_HT20:
if (prev_chan == new_chan) {
ret = true;
}
break;
default:
DBGPRINTF_N("%s: Invalid bandwidth\n", __func__);
break;
}
return ret;
}
static void qdrv_set_channel(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
uint32_t freq_band;
uint32_t qtn_chan;
int handle_radar = !(IEEE80211_IS_CHAN_CACDONE(ic->ic_curchan)) &&
!(IEEE80211_IS_CHAN_CAC_IN_PROGRESS(ic->ic_curchan));
ic->sta_dfs_info.allow_measurement_report = false;
qtn_chan = qdrv_set_channel_setup(ic, ic->ic_curchan);
freq_band = qdrv_set_channel_freqband_setup(ic, ic->ic_curchan);
qw->tx_stats.tx_channel = ic->ic_curchan->ic_ieee;
/* store normal txpower for short range workaround */
qdrv_hostlink_store_txpow(qw, ic->ic_curchan->ic_maxpower_normal);
if (ic->ic_chan_compare_equality(ic, qdrv_radar_get_current_cac_chan(), ic->ic_curchan) == false) {
qdrv_radar_stop_active_cac();
}
if (handle_radar) {
qdrv_radar_before_newchan();
}
qdrv_hostlink_setchan(qw, freq_band, qtn_chan);
qdrv_radar_on_newchan();
qdrv_chan_occupy_record_finish(ic, ic->ic_curchan->ic_ieee);
if (!(ic->ic_flags & IEEE80211_F_SCAN)) {
ic->ic_chan_switch_record(ic, ic->ic_curchan, ic->ic_csw_reason);
qdrv_eventf((TAILQ_FIRST(&ic->ic_vaps))->iv_dev,
QEVT_COMMON_PREFIX" Channel Changed to %d",
ic->ic_curchan->ic_ieee);
/* Reset ocac_rx_state as we have moved to another channel and should start gathering afresh */
spin_lock(&ic->ic_ocac.ocac_lock);
memset(&ic->ic_ocac.ocac_rx_state, 0, sizeof(ic->ic_ocac.ocac_rx_state));
spin_unlock(&ic->ic_ocac.ocac_lock);
}
}
static void qtn_scan_start(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
if ((TAILQ_FIRST(&ic->ic_vaps))->iv_opmode == IEEE80211_M_STA) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "Sending SCAN START to MuC\n");
qdrv_hostlink_setscanmode(qw,1);
}
/* FIXME: Disabling MODE changes for now */
if (1)
return;
/* Fixme : Do proper support for STA scan on MuC */
if ((TAILQ_FIRST(&ic->ic_vaps))->iv_opmode == IEEE80211_M_HOSTAP) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "Sending SCAN START to MuC\n");
qdrv_hostlink_setscanmode(qw,1);
}
}
static void qtn_scan_end(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if ((TAILQ_FIRST(&ic->ic_vaps))->iv_opmode == IEEE80211_M_STA) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "Sending SCAN STOP to MuC\n");
qdrv_hostlink_setscanmode(qw,0);
}
/* FIXME: Disabling MODE changes for now */
if (1) {
return;
}
/* Fixme : Do proper support for STA scan on MuC */
if ((TAILQ_FIRST(&ic->ic_vaps))->iv_opmode == IEEE80211_M_HOSTAP) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "Sending SCAN STOP to MuC\n");
qdrv_hostlink_setscanmode(qw,0);
}
}
static struct host_txdesc * qdrv_wlan_get_mgt_txdesc(struct ieee80211com *ic,
struct ieee80211_node *ni, struct sk_buff *skb)
{
struct net_device *vdev;
struct qdrv_vap *qv;
struct host_txdesc *txdesc;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
vdev = qv->ndev;
skb->dev = vdev;
QTN_SKB_ENCAP(skb) = QTN_SKB_ENCAP_80211_DATA;
skb->dest_port = ni->ni_node_idx;
M_FLAG_SET(skb, M_NO_AMSDU);
/*
* These frames are inserted into the tx datapath on the MuC, not in qdrv.
* The node ID is unset in order to avoid multiple node free operations during tx_done.
*/
QTN_SKB_CB_NI(skb) = NULL;
local_bh_disable();
txdesc = qdrv_tx_get_mgt_txdesc(skb, vdev);
local_bh_enable();
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return txdesc;
}
static int qdrv_wlan_get_ocs_frame(struct ieee80211com *ic, struct ieee80211_node *ni,
struct sk_buff *skb, uint32_t *frame_host, uint32_t *frame_bus,
uint16_t *frame_len, uint16_t *node_idx)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
if (QDRV_WLAN_TX_USE_AUC(qw))
{
void *buf_virt;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
uintptr_t flush_start;
size_t flush_size;
#endif
buf_virt = topaz_hbm_get_payload_virt(TOPAZ_HBM_BUF_EMAC_RX_POOL);
if (unlikely(!buf_virt)) {
return -1;
}
memcpy(buf_virt, skb->data, skb->len);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
dma_cache_wback_inv((unsigned long) buf_virt, skb->len);
#else
flush_start = (uintptr_t) align_buf_cache(buf_virt);
flush_size = align_buf_cache_size(buf_virt, skb->len);
flush_and_inv_dcache_range(flush_start, flush_start + flush_size);
#endif
*frame_host = (uint32_t)buf_virt;
*frame_bus = (uint32_t)virt_to_bus(buf_virt);
*frame_len = skb->len;
*node_idx = IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx);
dev_kfree_skb(skb);
}
else
{
struct host_txdesc *txdesc = qdrv_wlan_get_mgt_txdesc(ic, ni, skb);
if (!txdesc) {
return -1;
}
*frame_host = (uint32_t)txdesc->hd_va;
*frame_bus = (uint32_t)txdesc->hd_pa;
*frame_len = txdesc->hd_pktlen;
*node_idx = txdesc->hd_node_idx;
}
return 0;
}
static void qdrv_wlan_release_ocs_frame(struct ieee80211com *ic, uint32_t frame_host)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
if (QDRV_WLAN_TX_USE_AUC(qw))
{
void *buf_bus = (void *)virt_to_bus((void *)frame_host);
const int8_t pool = topaz_hbm_payload_get_pool_bus(buf_bus);
if (unlikely(!topaz_hbm_pool_valid(pool))) {
printk("%s: buf %x is not from hbm pool!\n", __FUNCTION__, frame_host);
} else {
topaz_hbm_put_payload_realign_bus(buf_bus, pool);
}
}
else
{
local_bh_disable();
qdrv_tx_release_txdesc(qw,
(struct lhost_txdesc *)frame_host);
local_bh_enable();
}
}
#if defined(QBMPS_ENABLE)
/*
* qdrv_bmps_release_frame: release the null frame queued in sp->bmps_lhost
* return 0: succeed; -1: failed
*/
static int qdrv_bmps_release_frame(struct ieee80211com *ic)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_bmps_info *bmps = sp->bmps_lhost;
if (bmps->state != BMPS_STATE_OFF) {
printk("%s: release frame error - status: %d!\n", __FUNCTION__, bmps->state);
return -1;
}
if (bmps->null_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, bmps->null_txdesc_host);
bmps->null_txdesc_host = 0;
bmps->null_txdesc_bus = 0;
}
return 0;
}
/*
* qdrv_bmps_set_frame: set the frame to sp->bmps_lhost
* NOTE: MUST be called with a reference to the node entry within
* the SKB CB structure, and free the reference to the node entry
* after this calling.
* return 0: succeed; -1: faild, need free skb by the caller.
*/
static int qdrv_bmps_set_frame(struct ieee80211com *ic,
struct ieee80211_node *ni, struct sk_buff *skb)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_bmps_info *bmps = sp->bmps_lhost;
if (bmps->state != BMPS_STATE_OFF) {
printk("%s: set frame error - status: %d!\n", __FUNCTION__, bmps->state);
return -1;
}
if (!skb) {
printk("%s: set frame error - skb is null\n", __FUNCTION__);
return -1;
}
/*
* Release the previous null frame if it has not been released,
* and then get one new tx descriptor
*/
if (qdrv_bmps_release_frame(ic) != 0) {
printk("%s: set frame error - release previous frame fail!\n", __FUNCTION__);
return -1;
}
if (qdrv_wlan_get_ocs_frame(ic, ni, skb,
&bmps->null_txdesc_host, &bmps->null_txdesc_bus,
&bmps->null_frame_len, &bmps->tx_node_idx)) {
printk("%s: set frame error - no ocs frame\n", __FUNCTION__);
return -1;
}
return 0;
}
#endif
#ifdef QSCS_ENABLED
/*
* qdrv_scs_release_frame: release the qosnull frame queued in sp->chan_sample_lhost
* return 0: succeed; -1: failed
*/
static int qdrv_scs_release_frame(struct ieee80211com *ic, int force)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *sample = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &sample->base;
if (!force && off_chan_info->muc_status != QTN_CCA_STATUS_IDLE) {
SCSDBG(SCSLOG_INFO, "release frame error - status: %u!\n",
off_chan_info->muc_status);
return -1;
}
if (sample->qosnull_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, sample->qosnull_txdesc_host);
sample->qosnull_txdesc_host = 0;
sample->qosnull_txdesc_bus = 0;
}
SCSDBG(SCSLOG_VERBOSE, "qosnull frame released.\n");
return 0;
}
/*
* qdrv_scs_set_frame: set the frame to sp->chan_sample_lhost
* return 0: succeed; -1: failed.
*/
static int
qdrv_scs_set_frame(struct ieee80211vap *vap, struct qtn_samp_chan_info *sample)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct qtn_off_chan_info *off_chan_info = &sample->base;
struct sk_buff *skb = NULL;
int ret = -1;
if (off_chan_info->muc_status != QTN_CCA_STATUS_IDLE) {
SCSDBG(SCSLOG_INFO, "set frame error - status=%u\n",
off_chan_info->muc_status);
return -1;
}
if (sample->qosnull_txdesc_host) {
SCSDBG(SCSLOG_NOTICE, "Qos Null frame already configured -ignore\n");
return 0;
}
ieee80211_ref_node(ni);
/* set qosnull frame */
skb = ieee80211_get_qosnulldata(ni, WME_AC_VO);
if (!skb) {
SCSDBG(SCSLOG_NOTICE, "get qosnulldata skb error\n");
goto done;
}
if (qdrv_wlan_get_ocs_frame(ic, ni, skb,
&sample->qosnull_txdesc_host, &sample->qosnull_txdesc_bus,
&sample->qosnull_frame_len, &sample->tx_node_idx)) {
dev_kfree_skb_irq(skb);
SCSDBG(SCSLOG_NOTICE, "set frame error - no ocs frame\n");
goto done;
}
SCSDBG(SCSLOG_VERBOSE, "set qosnull frame successfully.\n");
ret = 0;
done:
ieee80211_free_node(ni);
return ret;
}
static void qdrv_scs_update_scan_stats(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
if (qdrv_hostlink_send_ioctl_args(qw, IOCTL_DEV_SCS_UPDATE_SCAN_STATS, 0, 0)) {
SCSDBG(SCSLOG_INFO, "IOCTL_DEV_SCS_UPDATE_SCAN_STATS failed\n");
}
}
static int qtn_is_traffic_heavy_for_sampling(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_mac *mac = qw->mac;
struct net_device *dev;
struct netdev_queue *txq;
struct Qdisc *sch;
u_int32_t i, total_queued_pkts = 0;
struct ap_state *as = ic->ic_scan->ss_scs_priv;
/* check the airtime of this bss */
if (as->as_tx_ms_smth > ic->ic_scs.scs_thrshld_smpl_airtime ||
as->as_rx_ms_smth > ic->ic_scs.scs_thrshld_smpl_airtime) {
SCSDBG(SCSLOG_INFO, "not sampling - tx_ms_smth: %u, rx_ms_smth: %u\n",
as->as_tx_ms_smth, as->as_rx_ms_smth);
return 1;
}
/* check the packet number in tx queue */
for (i = 0; i <= mac->vnet_last; ++i) {
dev = mac->vnet[i];
if (dev && (dev->flags & IFF_UP)) {
txq = netdev_get_tx_queue(dev, 0);
sch = txq->qdisc;
if (sch) {
total_queued_pkts += sch->q.qlen;
}
}
}
if (total_queued_pkts > ic->ic_scs.scs_thrshld_smpl_pktnum) {
SCSDBG(SCSLOG_INFO, "not sampling - queued packet number: %u\n",
total_queued_pkts);
return 1;
}
return 0;
}
/*
* disable radar detection and tx queue,
* trigger a cca read to start.
* returns 0 if a cca read ioctl was sent to the MuC successfully,
* < 0 if we are currently performing a cca read or other error
*/
int qdrv_async_cca_read(struct ieee80211com *ic, const struct ieee80211_channel *cca_channel,
u_int64_t start_tsf, u_int32_t duration)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *sample = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &sample->base;
if (ic == NULL || cca_channel == NULL || duration == 0) {
return -EINVAL;
}
/* sample channel not allowed in power-saving mode */
if (((ic->ic_opmode == IEEE80211_M_HOSTAP)
#if defined(QBMPS_ENABLE)
|| (ic->ic_opmode == IEEE80211_M_STA)
#endif
) && (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_DUTY)) {
SCSDBG(SCSLOG_INFO, "not sampling - CoC idle\n");
return -EWOULDBLOCK;
}
if (off_chan_info->muc_status != QTN_CCA_STATUS_IDLE) {
return -EWOULDBLOCK;
}
sample->start_tsf = start_tsf;
off_chan_info->dwell_msecs = duration;
off_chan_info->freq_band = qdrv_set_channel_freqband_setup(ic, cca_channel);
off_chan_info->channel = qdrv_set_channel_setup(ic, cca_channel);
off_chan_info->flags = QTN_OFF_CHAN_FLAG_PASSIVE_ONESHOT;
sample->type = QTN_CCA_TYPE_DIRECTLY;
/*
* TODO SCS: radar.
*/
off_chan_info->muc_status = QTN_CCA_STATUS_HOST_IOCTL_SENT;
if (qdrv_hostlink_sample_chan(qw, sp->chan_sample_bus) >= 0) {
off_chan_info->muc_status = QTN_CCA_STATUS_IDLE;
}
/* indicate sample channel is on-going */
ic->ic_flags_qtn |= IEEE80211_QTN_SAMP_CHAN;
return 0;
}
EXPORT_SYMBOL(qdrv_async_cca_read);
static int qdrv_sample_channel_cancel(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *sample = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &sample->base;
if (qdrv_hostlink_sample_chan_cancel(qw, sp->chan_sample_bus) < 0) {
off_chan_info->muc_status = QTN_CCA_STATUS_IDLE;
SCSDBG(SCSLOG_INFO, "hostlink cancel off channel sampling error!\n");
return -1;
}
SCSDBG(SCSLOG_INFO, "cancel off channel sampling\n");
return 0;
}
static int qdrv_sample_channel(struct ieee80211vap *vap, struct ieee80211_channel *sampled_channel)
{
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *sample = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &sample->base;
QDRV_SCS_LOG_TSF(sample, SCS_LOG_TSF_POS_LHOST_TASK_KICKOFF);
if (ic == NULL || sampled_channel == NULL) {
return -1;
}
if ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
) {
SCSDBG(SCSLOG_INFO, "not sampling - scan in progress\n");
return -1;
}
if (!qdrv_radar_can_sample_chan()) {
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_RADAR);
SCSDBG(SCSLOG_INFO, "not sampling - radar\n");
return -1;
}
if (qtn_is_traffic_heavy_for_sampling(ic)) {
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_TRAFFIC_HEAVY);
SCSDBG(SCSLOG_INFO, "not sampling - traffic too heavy\n");
return -1;
}
/* sample channel not allowed in power-saving mode */
if (((ic->ic_opmode == IEEE80211_M_HOSTAP)
#if defined(QBMPS_ENABLE)
|| (ic->ic_opmode == IEEE80211_M_STA)
#endif
) && (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_DUTY)) {
SCSDBG(SCSLOG_INFO, "not sampling - CoC idle\n");
return -1;
}
if (off_chan_info->muc_status != QTN_CCA_STATUS_IDLE) {
SCSDBG(SCSLOG_INFO, "not sampling - sampling in progress!\n");
return -1;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "sampling channel %u\n", sampled_channel->ic_ieee);
if (!sample->qosnull_txdesc_host && qdrv_scs_set_frame(vap, sample)) {
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_QOSNULL_NOTREADY);
SCSDBG(SCSLOG_INFO, "not sampling - set qosnull frame error\n");
return -1;
}
sample->start_tsf = 0;
off_chan_info->dwell_msecs = ic->ic_scs.scs_smpl_dwell_time;
off_chan_info->freq_band = qdrv_set_channel_freqband_setup(ic, sampled_channel);
off_chan_info->channel = qdrv_set_channel_setup(ic, sampled_channel);
off_chan_info->flags = ic->ic_scs.scs_sample_type;
sample->type = QTN_CCA_TYPE_BACKGROUND;
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
!(off_chan_info->flags & QTN_OFF_CHAN_FLAG_ACTIVE)) {
off_chan_info->flags |= QTN_OFF_CHAN_TURNOFF_RF;
}
QDRV_SCS_LOG_TSF(sample, SCS_LOG_TSF_POS_LHOST_IOCTL2MUC);
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_IOCTL);
off_chan_info->muc_status = QTN_CCA_STATUS_HOST_IOCTL_SENT;
if (qdrv_hostlink_sample_chan(qw, sp->chan_sample_bus) < 0) {
off_chan_info->muc_status = QTN_CCA_STATUS_IDLE;
SCSDBG(SCSLOG_INFO, "hostlink sample channel error!\n");
return -1;
}
/* indicate sample channel is on-going */
ic->ic_flags_qtn |= IEEE80211_QTN_SAMP_CHAN;
ic->ic_chan_switch_reason_record(ic, IEEE80211_CSW_REASON_SAMPLING);
return 0;
}
#endif /* QSCS_ENABLED */
#ifdef QTN_BG_SCAN
/*
* qdrv_bgscan_start: reset bgscan status if it is not idle or completed
* return 0: succeed; -1: failed
*/
static int qdrv_bgscan_start(struct ieee80211com *ic)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
struct qtn_off_chan_info *off_chan_info = &scan_host->base;
if (off_chan_info->muc_status != QTN_SCAN_CHAN_MUC_IDLE
&& off_chan_info->muc_status != QTN_SCAN_CHAN_MUC_COMPLETED) {
printk("BG_SCAN: status (%u) is not idle or completed!\n",
off_chan_info->muc_status);
off_chan_info->muc_status = QTN_SCAN_CHAN_MUC_IDLE;
}
return 0;
}
/*
* qdrv_bgscan_release_frame: delete the frame queued in sp->chan_scan_lhost
* return 0: succeed; -1: failed
*/
static int qdrv_bgscan_release_frame(struct ieee80211com *ic, int frm_flag, int force)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
struct qtn_off_chan_info *off_chan_info = &scan_host->base;
if (!force && off_chan_info->muc_status != QTN_SCAN_CHAN_MUC_IDLE
&& off_chan_info->muc_status != QTN_SCAN_CHAN_MUC_COMPLETED) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: release frame error for current muc_status: %u!\n",
off_chan_info->muc_status);
}
return -1;
}
if (frm_flag == IEEE80211_SCAN_FRAME_START
|| frm_flag == IEEE80211_SCAN_FRAME_ALL) {
if (scan_host->start_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, scan_host->start_txdesc_host);
scan_host->start_txdesc_host = 0;
scan_host->start_txdesc_bus = 0;
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("BG_SCAN: delete start frame!\n");
}
}
}
if (frm_flag == IEEE80211_SCAN_FRAME_PRBREQ
|| frm_flag == IEEE80211_SCAN_FRAME_ALL) {
if (scan_host->prbreq_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, scan_host->prbreq_txdesc_host);
scan_host->prbreq_txdesc_host = 0;
scan_host->prbreq_txdesc_bus = 0;
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("BG_SCAN: delete prbreq frame!\n");
}
}
}
if (frm_flag == IEEE80211_SCAN_FRAME_FINISH
|| frm_flag == IEEE80211_SCAN_FRAME_ALL) {
if (scan_host->finish_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, scan_host->finish_txdesc_host);
scan_host->finish_txdesc_host = 0;
scan_host->finish_txdesc_bus = 0;
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("BG_SCAN: delete finish frame!\n");
}
}
}
return 0;
}
/*
* qdrv_bgscan_set_frame: set the frame to sp->chan_scan_lhost
* NOTE: MUST be called with a reference to the node entry within
* the SKB CB structure, and free the reference to the node entry
* after this calling.
* return 0: succeed; -1: faild, need free skb by the caller.
*/
static int qdrv_bgscan_set_frame(struct ieee80211com *ic,
struct ieee80211_node *ni, struct sk_buff *skb, int frm_flag)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
uint32_t frame_host;
uint32_t frame_bus;
uint16_t frame_len;
uint16_t node_idx;
if (qdrv_wlan_get_ocs_frame(ic, ni, skb,
&frame_host, &frame_bus, &frame_len, &node_idx)) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: set frame error - no ocs frame\n");
}
return -1;
}
switch (frm_flag) {
case IEEE80211_SCAN_FRAME_START:
scan_host->start_txdesc_host = frame_host;
scan_host->start_txdesc_bus = frame_bus;
scan_host->start_frame_len = frame_len;
scan_host->start_node_idx = node_idx;
break;
case IEEE80211_SCAN_FRAME_PRBREQ:
scan_host->prbreq_txdesc_host = frame_host;
scan_host->prbreq_txdesc_bus = frame_bus;
scan_host->prbreq_frame_len = frame_len;
scan_host->prbreq_node_idx = node_idx;
break;
case IEEE80211_SCAN_FRAME_FINISH:
scan_host->finish_txdesc_host = frame_host;
scan_host->finish_txdesc_bus = frame_bus;
scan_host->finish_frame_len = frame_len;
scan_host->finish_node_idx = node_idx;
break;
}
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("BG_SCAN: set frame flag %u\n", frm_flag);
}
return 0;
}
static int
qdrv_bgscan_init_frames(struct ieee80211vap *vap, struct qtn_scan_chan_info *scan_host)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct net_device *dev = vap->iv_dev;
struct sk_buff *skb = NULL;
int ret = -1;
ieee80211_ref_node(ni);
if (!scan_host->start_txdesc_host) {
/* set start frame */
skb = ieee80211_get_qosnulldata(ni, WME_AC_VO);
if (!skb) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: get qosnulldata skb error\n");
}
goto done;
}
if (qdrv_bgscan_set_frame(ic, ni, skb, IEEE80211_SCAN_FRAME_START)) {
dev_kfree_skb_irq(skb);
goto done;
}
}
if (!scan_host->prbreq_txdesc_host) {
/* set probe request frame */
skb = ieee80211_get_probereq(vap->iv_bss,
vap->iv_myaddr, dev->broadcast,
dev->broadcast, (u_int8_t *)"", 0,
vap->iv_opt_ie, vap->iv_opt_ie_len);
if (!skb) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: get probereq skb error\n");
}
goto done;
}
if (qdrv_bgscan_set_frame(ic, ni, skb, IEEE80211_SCAN_FRAME_PRBREQ)) {
dev_kfree_skb_irq(skb);
goto done;
}
}
ret = 0;
done:
ieee80211_free_node(ni);
return ret;
}
enum scan_mode_index {
SCAN_MODE_INDEX_ACTIVE = 0,
SCAN_MODE_INDEX_PASSIVE_FAST,
SCAN_MODE_INDEX_PASSIVE_NORMAL,
SCAN_MODE_INDEX_PASSIVE_SLOW
};
static char *scan_mode_str[] = {
"active",
"passive_fast",
"passive_normal",
"passive_slow"
};
static int qdrv_bgscan_channel(struct ieee80211vap *vap,
struct ieee80211_channel *scanned_channel, int scan_mode, int dwelltime)
{
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
struct qtn_off_chan_info *off_chan_host = &scan_host->base;
int mode_index;
if ((vap->iv_state != IEEE80211_S_RUN) && !(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER))
return -1;
if (scanned_channel == NULL) {
if (ic->ic_qtn_bgscan.debug_flags >= 1)
printk("BG_SCAN: stop - wrong parameters\n");
return -1;
}
if (ic->ic_flags & IEEE80211_F_SCAN) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: stop - scan in progress\n");
}
return -1;
}
if (!qdrv_radar_can_sample_chan()) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: stop scan - radar\n");
}
return -1;
}
if (off_chan_host->muc_status != QTN_SCAN_CHAN_MUC_IDLE
&& off_chan_host->muc_status != QTN_SCAN_CHAN_MUC_COMPLETED) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: stop scan - status=%u\n",
off_chan_host->muc_status);
}
return -1;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "scanning channel %u\n", scanned_channel->ic_ieee);
if (!scan_host->start_txdesc_host || !scan_host->prbreq_txdesc_host) {
if (qdrv_bgscan_init_frames(vap, scan_host)) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: Initiate scan frames error\n");
}
return -1;
}
}
off_chan_host->freq_band = qdrv_set_channel_freqband_setup(ic, scanned_channel);
off_chan_host->channel = qdrv_set_channel_setup(ic, scanned_channel);
off_chan_host->dwell_msecs = dwelltime;
scan_host->start_node_idx = IEEE80211_NODE_IDX_UNMAP(vap->iv_bss->ni_node_idx) ? IEEE80211_NODE_IDX_UNMAP(vap->iv_bss->ni_node_idx) :
IEEE80211_NODE_IDX_UNMAP(vap->iv_vapnode_idx);
scan_host->prbreq_node_idx = scan_host->start_node_idx;
scan_host->finish_node_idx = scan_host->start_node_idx;
off_chan_host->flags = 0;
if (scan_mode & IEEE80211_PICK_BG_ACTIVE) {
off_chan_host->flags |= QTN_OFF_CHAN_FLAG_ACTIVE;
mode_index = SCAN_MODE_INDEX_ACTIVE;
} else if (scan_mode & IEEE80211_PICK_BG_PASSIVE_FAST) {
off_chan_host->flags |= QTN_OFF_CHAN_FLAG_PASSIVE_FAST;
mode_index = SCAN_MODE_INDEX_PASSIVE_FAST;
} else if (scan_mode & IEEE80211_PICK_BG_PASSIVE_NORMAL) {
off_chan_host->flags |= QTN_OFF_CHAN_FLAG_PASSIVE_NORMAL;
mode_index = SCAN_MODE_INDEX_PASSIVE_NORMAL;
} else {
off_chan_host->flags |= QTN_OFF_CHAN_FLAG_PASSIVE_SLOW;
mode_index = SCAN_MODE_INDEX_PASSIVE_SLOW;
}
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
!(off_chan_host->flags & QTN_OFF_CHAN_FLAG_ACTIVE)) {
off_chan_host->flags |= QTN_OFF_CHAN_TURNOFF_RF;
}
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("scan channel %u, scan mode: %s, cca_idle: %u\n",
scanned_channel->ic_ieee, scan_mode_str[mode_index],
ic->ic_scs.scs_cca_idle_smthed);
}
IEEE80211_LOCK_IRQ(ic);
ic->ic_flags |= IEEE80211_F_SCAN;
IEEE80211_UNLOCK_IRQ(ic);
QDRV_SCAN_LOG_TSF(scan_host, SCAN_CHAN_TSF_LHOST_HOSTLINK_IOCTL);
if (qdrv_hostlink_bgscan_chan(qw, sp->chan_scan_bus) < 0) {
IEEE80211_LOCK_IRQ(ic);
ic->ic_flags &= ~IEEE80211_F_SCAN;
IEEE80211_UNLOCK_IRQ(ic);
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: hostlink error\n");
}
return -1;
}
ic->ic_chan_switch_reason_record(ic, IEEE80211_CSW_REASON_BGSCAN);
return 0;
}
#endif /* QTN_BG_SCAN */
static void qdrv_update_sta_dfs_strict_flags(struct ieee80211com *ic)
{
if (ic->sta_dfs_info.sta_dfs_radar_detected_timer) {
ic->sta_dfs_info.sta_dfs_radar_detected_timer = false;
del_timer(&ic->sta_dfs_info.sta_radar_timer);
if (ic->ic_mark_channel_availability_status) {
struct ieee80211_channel *chan = ieee80211_find_channel_by_ieee(ic,
ic->sta_dfs_info.sta_dfs_radar_detected_channel);
ic->ic_mark_channel_availability_status(ic, chan,
IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_RADAR_DETECTED);
ic->sta_dfs_info.sta_dfs_radar_detected_channel = 0;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "\n%s: sta_radar_timer: deleted\n", __func__);
}
if (ic->ic_chan_compare_equality(ic, ic->ic_curchan, ic->ic_prevchan) == false) {
if (ic->ic_mark_channel_availability_status) {
if (ieee80211_is_chan_not_available(ic->ic_curchan)) {
ic->ic_mark_channel_availability_status(ic,
ic->ic_curchan,
IEEE80211_CHANNEL_STATUS_AVAILABLE);
}
if (ieee80211_is_chan_available(ic->ic_prevchan)) {
ic->ic_mark_channel_availability_status(ic,
ic->ic_prevchan,
IEEE80211_CHANNEL_STATUS_NON_AVAILABLE);
}
}
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s: qdrv_set_channel_deferred: "
"ic->ic_prevchan = %d, ic->ic_curchan = %d\n",
__func__, ic->ic_prevchan->ic_ieee, ic->ic_curchan->ic_ieee);
ic->sta_dfs_info.allow_measurement_report = false;
}
/*
* deferred channel change, where the MuC handles the channel change,
* aiming to change at a particular tsf, and notifies the lhost when it occurs.
*/
static void qdrv_set_channel_deferred(struct ieee80211com *ic, u64 tsf, int csaflags)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_csa_info *csa = sp->csa_lhost;
unsigned long irqflags;
int newchan_radar = qdrv_radar_is_rdetection_required(ic->ic_csa_chan);
uint8_t prev_ieee = ic->ic_curchan->ic_ieee;
uint8_t cur_ieee = ic->ic_csa_chan->ic_ieee;
spin_lock_irqsave(&qw->csa_lock, irqflags);
if (csaflags & IEEE80211_SET_CHANNEL_DEFERRED_CANCEL) {
/*
* the muc may pick this up, but if it doesn't it will
* complete the entire channel change
*/
csa->lhost_status |= QTN_CSA_CANCEL;
spin_unlock_irqrestore(&qw->csa_lock, irqflags);
return;
}
if (csa->lhost_status & QTN_CSA_STATUS_LHOST_ACTIVE) {
/* csa in progress */
spin_unlock_irqrestore(&qw->csa_lock, irqflags);
DBGPRINTF_E("CSA already active\n");
return;
}
csa->lhost_status = QTN_CSA_STATUS_LHOST_ACTIVE;
if (csaflags & IEEE80211_SET_CHANNEL_TSF_OFFSET) {
csa->lhost_status |= QTN_CSA_STATUS_LHOST_UNITS_OFFSET;
}
if (!newchan_radar) {
csa->lhost_status |= QTN_CSA_RESTART_QUEUE;
}
spin_unlock_irqrestore(&qw->csa_lock, irqflags);
ic->ic_prevchan = ic->ic_curchan;
ic->ic_curchan = ic->ic_des_chan = ic->ic_csa_chan;
csa->channel = qdrv_set_channel_setup(ic, ic->ic_curchan);
csa->freq_band = qdrv_set_channel_freqband_setup(ic, ic->ic_curchan);
csa->req_tsf = tsf;
csa->pre_notification_tu = 10; /* Time Units */
csa->post_notification_tu = 10;
if (ic->sta_dfs_info.sta_dfs_strict_mode) {
qdrv_update_sta_dfs_strict_flags(ic);
}
qdrv_hostlink_setchan_deferred(qw, sp->csa_bus);
ic->ic_aci_cci_cce.cce_previous = prev_ieee;
ic->ic_aci_cci_cce.cce_current = cur_ieee;
}
static void csa_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, csa_wq);
struct ieee80211com *ic = &qw->ic;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_csa_info *csa = sp->csa_lhost;
struct ieee80211_channel *chan = qw->ic.ic_curchan;
unsigned long irqflags;
u64 tsf = csa->switch_tsf;
#if defined(CONFIG_QTN_80211K_SUPPORT)
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
#endif
/* mark the entire process as done */
spin_lock_irqsave(&qw->csa_lock, irqflags);
csa->lhost_status = 0;
/* clear csa count at last to avoid any possibility of dual CS */
ic->ic_csa_count = 0;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "total rx CSA frame: beacon=%d, action=%d\n",
ic->ic_csa_frame[IEEE80211_CSA_FRM_BEACON],
ic->ic_csa_frame[IEEE80211_CSA_FRM_ACTION]);
memset(ic->ic_csa_frame, 0x0, sizeof(ic->ic_csa_frame));
spin_unlock_irqrestore(&qw->csa_lock, irqflags);
if (qw->csa_callback) {
qw->csa_callback(chan, tsf);
}
#if defined(CONFIG_QTN_80211K_SUPPORT)
if (vap && vap->iv_state == IEEE80211_S_RUN)
ieee80211_send_action_dfs_report(vap->iv_bss);
#endif
}
static void channel_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, channel_work_wq);
struct ieee80211com *ic = &qw->ic;
ieee80211_channel_switch_post(ic);
qdrv_radar_on_newchan();
}
static void remain_channel_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, remain_chan_wq);
struct ieee80211com *ic = &qw->ic;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
ieee80211_sta_pwrsave(vap, 0);
vap->tdls_chan_switching = 0;
vap->tdls_cs_node = NULL;
if (vap->tdls_cs_disassoc_pending == 1) {
vap->tdls_cs_disassoc_pending = 0;
vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
}
#if defined(QBMPS_ENABLE)
/* indicate sample channel is done */
/* start power-saving if allowed */
ic->ic_flags_qtn &= ~IEEE80211_QTN_SAMP_CHAN;
ic->ic_pm_reason = IEEE80211_PM_LEVEL_REMAIN_CHANNEL_WORK;
ieee80211_pm_queue_work(ic);
#endif
}
static void qdrv_csa_irqhandler(void *arg1, void *arg2)
{
struct qdrv_wlan *qw = arg1;
struct ieee80211com *ic = &qw->ic;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_remain_chan_info *rc = sp->remain_chan_lhost;
struct qtn_csa_info *csa = sp->csa_lhost;
struct ieee80211_channel *chan = NULL;
u32 muc_status = csa->muc_status;
u32 lhost_status = csa->lhost_status;
if (rc->status == QTN_REM_CHAN_STATUS_MUC_STARTED) {
chan = ic->ic_findchannel(ic,
QTNCHAN_TO_IEEENUM(rc->off_channel), ic->ic_des_mode);
if (chan) {
ic->ic_curchan = chan;
vap->tdls_chan_switching = 1;
}
return;
} else if ((rc->status == QTN_REM_CHAN_STATUS_MUC_COMPLETE) ||
(rc->status == QTN_REM_CHAN_STATUS_MUC_CANCELLED)) {
rc->status = QTN_REM_CHAN_STATUS_IDLE;
ic->ic_curchan = ic->ic_bsschan;
schedule_work(&qw->remain_chan_wq);
return;
}
if (muc_status & QTN_CSA_STATUS_MUC_PRE &&
!(lhost_status & QTN_CSA_STATUS_LHOST_PRE_DONE)) {
qdrv_radar_before_newchan();
lhost_status |= QTN_CSA_STATUS_LHOST_PRE_DONE;
}
if (muc_status & QTN_CSA_STATUS_MUC_SWITCHED &&
!(lhost_status & QTN_CSA_STATUS_LHOST_SWITCH_DONE)) {
/* just switched channel, restart radar */
qw->tx_stats.tx_channel = ic->ic_curchan->ic_ieee;
schedule_work(&qw->channel_work_wq);
lhost_status |= QTN_CSA_STATUS_LHOST_SWITCH_DONE;
}
if (muc_status & QTN_CSA_STATUS_MUC_POST &&
!(lhost_status & QTN_CSA_STATUS_LHOST_POST_DONE)) {
/* all done! */
lhost_status |= QTN_CSA_STATUS_LHOST_POST_DONE;
/* call workqueue to handle callback */
schedule_work(&qw->csa_wq);
}
/* successfully cancelled */
if ((muc_status & QTN_CSA_STATUS_MUC_CANCELLED) &&
(muc_status & QTN_CSA_STATUS_MUC_COMPLETE)) {
lhost_status = 0;
ic->ic_csa_count = 0;
memset(ic->ic_csa_frame, 0x0, sizeof(ic->ic_csa_frame));
}
csa->lhost_status = lhost_status;
}
static int qdrv_init_csa_irqhandler(struct qdrv_wlan *qw)
{
struct int_handler int_handler;
int_handler.handler = qdrv_csa_irqhandler;
int_handler.arg1 = qw;
int_handler.arg2 = NULL;
if (qdrv_mac_set_handler(qw->mac, RUBY_M2L_IRQ_LO_CSA, &int_handler) != 0) {
DBGPRINTF_E("Could not set csa irq handler\n");
return -EINVAL;
}
qdrv_mac_enable_irq(qw->mac, RUBY_M2L_IRQ_LO_CSA);
return 0;
}
static struct off_chan_tsf_dbg scs_tsf_index_name[] = {
{SCS_LOG_TSF_POS_LHOST_TASK_KICKOFF, "host_task_start"},
{SCS_LOG_TSF_POS_LHOST_IOCTL2MUC, "host_send_ioctl"},
{SCS_LOG_TSF_POS_MUC_POLL_IOCTL_FROM_LHOST, "muc_ioctl_proc"},
{SCS_LOG_TSF_POS_MUC_QOSNULL_SENT, "muc_qosnull_sent"},
{SCS_LOG_TSF_POS_MUC_SMPL_START_BEFORE_CHAN_CHG, "muc_goto_offchan"},
{SCS_LOG_TSF_POS_MUC_SMPL_START_AFTER_CHAN_CHG, "muc_offchan_done"},
{SCS_LOG_TSF_POS_MUC_SMPL_FINISH_BEFORE_CHAN_CHG, "muc_goto_datachan"},
{SCS_LOG_TSF_POS_MUC_SMPL_FINISH_AFTER_CHAN_CHG, "muc_datachan_done"},
{SCS_LOG_TSF_POS_LHOST_CCA_INTR, "host_interrupt"},
{SCS_LOG_TSF_POS_LHOST_CCA_WORK, "host_scswork"},
{0,NULL}
};
static void cca_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, cca_wq);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *sample = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &sample->base;
struct ieee80211com *ic = &qw->ic;
struct ieee80211vap *vap;
uint32_t tsf_hi;
uint32_t tsf_lo;
uint32_t delta;
uint32_t cur_index;
uint32_t pre_index = 0;
int i;
if (off_chan_info->muc_status == QTN_CCA_STATUS_MUC_COMPLETE) {
QDRV_SCS_LOG_TSF(sample, SCS_LOG_TSF_POS_LHOST_CCA_WORK);
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_COMPLETE);
ic->ic_scs.scs_last_smpl_chan = ic->ic_scs.scs_des_smpl_chan;
if (ic->ic_flags & IEEE80211_F_CCA) {
ic->ic_flags &= ~IEEE80211_F_CCA;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if ((vap->iv_state != IEEE80211_S_RUN) && (vap->iv_state != IEEE80211_S_SCAN))
continue;
ic->ic_beacon_update(vap);
}
}
SCSDBG(SCSLOG_INFO, "Sample channel %u completed\n",
QTNCHAN_TO_IEEENUM(off_chan_info->channel));
off_chan_info->muc_status = QTN_CCA_STATUS_IDLE;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "Sampling finished\n");
if (ic->ic_scs.scs_debug_enable == 3) {
for (i = 0; scs_tsf_index_name[i].log_name; i++) {
cur_index = scs_tsf_index_name[i].pos_index;
tsf_hi = U64_HIGH32(sample->tsf[cur_index]);
tsf_lo = U64_LOW32(sample->tsf[cur_index]);
if (i) {
delta = (uint32_t)(sample->tsf[cur_index] -
sample->tsf[pre_index]);
} else {
delta = 0;
printk("\n\nSCS_SAMPLE_tsf:\n");
}
pre_index = cur_index;
printk(" %s: %08x_%08x (+%u)\n",
scs_tsf_index_name[i].log_name, tsf_hi, tsf_lo, delta);
}
}
} else if (off_chan_info->muc_status == QTN_CCA_STATUS_MUC_CANCELLED) {
SCSDBG(SCSLOG_INFO, "Sample channel %u cancelled\n",
QTNCHAN_TO_IEEENUM(off_chan_info->channel));
off_chan_info->muc_status = QTN_CCA_STATUS_IDLE;
}
ic->ic_flags_qtn &= ~IEEE80211_QTN_SAMP_CHAN;
#if defined(QBMPS_ENABLE)
/* indicate sample channel is done */
/* start power-saving if allowed */
if (ic->ic_opmode == IEEE80211_M_STA) {
ic->ic_pm_reason = IEEE80211_PM_LEVEL_CCA_WORK;
ieee80211_pm_queue_work(ic);
}
#endif
}
static void qdrv_cca_irqhandler(void *arg1, void *arg2)
{
struct qdrv_wlan *qw = arg1;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_samp_chan_info *cca = sp->chan_sample_lhost;
struct qtn_off_chan_info *off_chan_info = &cca->base;
/*
* TODO SCS: radar disable during the cca read
*/
if (off_chan_info->muc_status == QTN_CCA_STATUS_MUC_COMPLETE ||
off_chan_info->muc_status == QTN_CCA_STATUS_MUC_CANCELLED) {
QDRV_SCS_LOG_TSF(cca, SCS_LOG_TSF_POS_LHOST_CCA_INTR);
schedule_work(&qw->cca_wq);
}
}
static int qdrv_init_cca_irqhandler(struct qdrv_wlan *qw)
{
struct int_handler int_handler;
int_handler.handler = qdrv_cca_irqhandler;
int_handler.arg1 = qw;
int_handler.arg2 = NULL;
if (qdrv_mac_set_handler(qw->mac, RUBY_M2L_IRQ_LO_SCS, &int_handler) != 0) {
DBGPRINTF_E("Could not set cca irq handler\n");
return -1;
}
qdrv_mac_enable_irq(qw->mac, RUBY_M2L_IRQ_LO_SCS);
return 0;
}
static char *meas_err_msg[] = {
"off-channel not supported",
"duration too short for measurement",
"macfw timer scheduled fail",
"measurement type unsupport"
};
static void meas_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, meas_wq);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_meas_chan_info *meas_info = sp->chan_meas_lhost;
struct ieee80211com *ic = &qw->ic;
struct ieee80211_global_measure_info *ic_meas_info = &ic->ic_measure_info;
if (ic_meas_info->status == MEAS_STATUS_DISCRAD) {
ic_meas_info->status = MEAS_STATUS_IDLE;
return;
}
if (meas_info->meas_reason == QTN_MEAS_REASON_SUCC) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "measurement success\n");
switch (meas_info->meas_type) {
case QTN_MEAS_TYPE_CCA:
{
u_int16_t cca_result;
cca_result = (uint16_t)meas_info->inter_data.cca_and_chanload.cca_busy_ms;
cca_result = cca_result * 1000 / meas_info->inter_data.cca_and_chanload.cca_try_ms;
cca_result = cca_result * 255 / 1000;
ic_meas_info->results.cca = (u_int8_t)cca_result;
break;
}
case QTN_MEAS_TYPE_RPI:
{
u_int32_t rpi_sum;
u_int32_t i;
rpi_sum = 0;
for (i = 0; i < 8; i++)
rpi_sum += meas_info->inter_data.rpi_counts[i];
if (rpi_sum == 0) {
memset(ic_meas_info->results.rpi, 0, sizeof(ic_meas_info->results.rpi));
} else {
for (i = 0; i < 8; i++)
ic_meas_info->results.rpi[i] = (u_int8_t)((meas_info->inter_data.rpi_counts[i] * 255) / rpi_sum);
}
break;
}
case QTN_MEAS_TYPE_BASIC:
{
int radar_num;
radar_num = ic->ic_radar_detections_num(ic_meas_info->param.basic.channel);
if ((radar_num >= 0) && ((radar_num - meas_info->inter_data.basic_radar_num) > 0))
ic_meas_info->results.basic |= IEEE80211_MEASURE_BASIC_REPORT_RADAR;
ic_meas_info->results.basic |= meas_info->inter_data.basic;
break;
}
case QTN_MEAS_TYPE_CHAN_LOAD:
{
u_int16_t cca_result;
cca_result = (uint16_t)meas_info->inter_data.cca_and_chanload.cca_busy_ms;
cca_result = cca_result * 1000 / meas_info->inter_data.cca_and_chanload.cca_try_ms;
cca_result = cca_result * 255 / 1000;
ic_meas_info->results.chan_load = (u_int8_t)cca_result;
break;
}
case QTN_MEAS_TYPE_NOISE_HIS:
{
int32_t local_noise = 0;
struct ieee80211_phy_stats phy_stats;
if (ic->ic_get_phy_stats
&& !ic->ic_get_phy_stats(ic->ic_dev, ic, &phy_stats, 0)) {
local_noise = (int32_t)phy_stats.rx_noise;
}
ic_meas_info->results.noise_his.anpi = ABS(local_noise) / 10;
memset(&ic_meas_info->results.noise_his.ipi, 0,
sizeof(ic_meas_info->results.noise_his.ipi));
break;
}
default:
break;
}
ic->ic_finish_measurement(ic, 0);
} else {
printk("measurement fail:%s\n",meas_err_msg[meas_info->meas_reason - QTN_MEAS_REASON_OFF_CHANNEL_UNSUPPORT]);
ic->ic_finish_measurement(ic, IEEE80211_CCA_REPMODE_REFUSE);
}
ic_meas_info->status = MEAS_STATUS_IDLE;
}
static void qdrv_meas_irqhandler(void *arg1, void *arg2)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)arg1;
schedule_work(&qw->meas_wq);
}
static int qdrv_init_meas_irqhandler(struct qdrv_wlan *qw)
{
struct int_handler int_handler;
int_handler.handler = qdrv_meas_irqhandler;
int_handler.arg1 = qw;
int_handler.arg2 = NULL;
if (qdrv_mac_set_handler(qw->mac, RUBY_M2L_IRQ_LO_MEAS, &int_handler) != 0) {
DBGPRINTF_E("Could not set measurement irq handler\n");
return -1;
}
qdrv_mac_enable_irq(qw->mac, RUBY_M2L_IRQ_LO_MEAS);
return 0;
}
#ifdef QTN_BG_SCAN
static struct off_chan_tsf_dbg scan_tsf_index_name[] = {
{SCAN_CHAN_TSF_LHOST_HOSTLINK_IOCTL, "host_send_ioctl"},
{SCAN_CHAN_TSF_MUC_IOCTL_PROCESS, "muc_ioctl_proc"},
{SCAN_CHAN_TSF_MUC_SEND_START_FRM, "muc_send_start"},
{SCAN_CHAN_TSF_MUC_SEND_START_FRM_DONE, "muc_start_done"},
{SCAN_CHAN_TSF_MUC_GOTO_OFF_CHAN, "muc_goto_offchan"},
{SCAN_CHAN_TSF_MUC_GOTO_OFF_CHAN_DONE, "muc_offchan_done"},
{SCAN_CHAN_TSF_MUC_SEND_PRBREQ_FRM, "muc_send_prbreq"},
{SCAN_CHAN_TSF_MUC_SEND_PRBREQ_FRM_DONE, "muc_prbreq_done"},
{SCAN_CHAN_TSF_MUC_GOTO_DATA_CHAN, "muc_goto_datachan"},
{SCAN_CHAN_TSF_MUC_GOTO_DATA_CHAN_DONE, "muc_datachan_done"},
{SCAN_CHAN_TSF_LHOST_INTERRUPT, "host_interrupt"},
{SCAN_CHAN_TSF_LHOST_SCANWORK, "host_scanwork"},
{0,NULL}
};
static void scan_work(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, scan_wq);
struct ieee80211com *ic = &qw->ic;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
struct qtn_off_chan_info *off_chan_info = &scan_host->base;
QDRV_SCAN_LOG_TSF(scan_host, SCAN_CHAN_TSF_LHOST_SCANWORK);
if (off_chan_info->muc_status == QTN_SCAN_CHAN_MUC_FAILED) {
if (ic->ic_qtn_bgscan.debug_flags >= 1) {
printk("BG_SCAN: scan channel %u failed!\n",
QTNCHAN_TO_IEEENUM(off_chan_info->channel));
}
off_chan_info->muc_status = QTN_SCAN_CHAN_MUC_IDLE;
} else if (ic->ic_qtn_bgscan.debug_flags == 2) {
u32 tsf_hi;
u32 tsf_lo;
u32 delta;
u32 cur_index;
u32 pre_index = 0;
int i;
for (i = 0; scan_tsf_index_name[i].log_name; i++) {
cur_index = scan_tsf_index_name[i].pos_index;
tsf_hi = U64_HIGH32(scan_host->tsf[cur_index]);
tsf_lo = U64_LOW32(scan_host->tsf[cur_index]);
if (i) {
delta = (u32)(scan_host->tsf[cur_index] -
scan_host->tsf[pre_index]);
} else {
delta = 0;
printk("\n\nBGSCAN_tsf:\n");
}
pre_index = cur_index;
printk(" %s: %08x_%08x (+%u)\n",
scan_tsf_index_name[i].log_name, tsf_hi, tsf_lo, delta);
}
}
}
static void qdrv_scan_irqhandler(void *arg1, void *arg2)
{
struct qdrv_wlan *qw = arg1;
struct ieee80211com *ic = &qw->ic;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_scan_chan_info *scan_host = sp->chan_scan_lhost;
u32 muc_status = scan_host->base.muc_status;
if (muc_status == QTN_SCAN_CHAN_MUC_COMPLETED
|| muc_status == QTN_SCAN_CHAN_MUC_FAILED) {
QDRV_SCAN_LOG_TSF(scan_host, SCAN_CHAN_TSF_LHOST_INTERRUPT);
ic->ic_flags &= ~IEEE80211_F_SCAN;
if (muc_status == QTN_SCAN_CHAN_MUC_FAILED ||
(ic->ic_qtn_bgscan.debug_flags == 2)) {
schedule_work(&qw->scan_wq);
}
}
}
static int qdrv_init_scan_irqhandler(struct qdrv_wlan *qw)
{
struct int_handler int_handler;
int_handler.handler = qdrv_scan_irqhandler;
int_handler.arg1 = qw;
int_handler.arg2 = NULL;
if (qdrv_mac_set_handler(qw->mac, RUBY_M2L_IRQ_LO_SCAN, &int_handler) != 0) {
DBGPRINTF_E("BG_SCAN: could not set irq handler\n");
return -1;
}
qdrv_mac_enable_irq(qw->mac, RUBY_M2L_IRQ_LO_SCAN);
return 0;
}
#endif /* QTN_BG_SCAN */
/*
* Set the qosnull frame to sp->ocac_lhost
* NOTE: MUST be called with a reference to the node entry within
* the SKB CB structure, and free the reference to the node entry
* after this call.
* Return 0 for success or -1 on failure.
* If failed, skb must be freed by the caller.
*/
static int qdrv_ocac_set_frame(struct ieee80211com *ic,
struct ieee80211_node *ni, struct sk_buff *skb)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_ocac_info *ocac_info = sp->ocac_lhost;
if (ocac_info->qosnull_txdesc_host) {
OCACDBG(OCACLOG_NOTICE, "qosnull frame exists\n");
return 0;
}
if (!skb) {
OCACDBG(OCACLOG_WARNING, "set frame error - skb is null\n");
return -1;
}
if (qdrv_wlan_get_ocs_frame(ic, ni, skb,
&ocac_info->qosnull_txdesc_host, &ocac_info->qosnull_txdesc_bus,
&ocac_info->qosnull_frame_len, &ocac_info->tx_node_idx)) {
OCACDBG(OCACLOG_WARNING, "set frame error - no ocs frame\n");
return -1;
}
OCACDBG(OCACLOG_VERBOSE, "set qosnull frame successfully\n");
return 0;
}
static int qdrv_ocac_release_frame(struct ieee80211com *ic, int force)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_ocac_info *ocac_info = sp->ocac_lhost;
if (!force && ic->ic_ocac.ocac_running) {
OCACDBG(OCACLOG_WARNING, "release frame error - CAC in progress!\n");
return -1;
}
if (ocac_info->qosnull_txdesc_host) {
qdrv_wlan_release_ocs_frame(ic, ocac_info->qosnull_txdesc_host);
ocac_info->qosnull_txdesc_host = 0;
ocac_info->qosnull_txdesc_bus = 0;
}
OCACDBG(OCACLOG_VERBOSE, "qosnull frame released.\n");
return 0;
}
static void qdrv_update_ocac_state_ie(struct ieee80211com *ic, uint8_t state, uint8_t param)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qdrv_hostlink_update_ocac_state_ie(qw, state, param);
}
static int qdrv_set_ocac(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_ocac_info *ocac_info = sp->ocac_lhost;
struct sk_buff *skb = NULL;
uint32_t off_channel;
struct ieee80211_ocac_params *ocac_params = &ic->ic_ocac.ocac_cfg.ocac_params;
if (chan) {
/* start ocac in MuC */
off_channel = qdrv_set_channel_setup(ic, chan);
if (off_channel == ocac_info->off_channel) {
ic->ic_ocac.ocac_counts.skip_set_run++;
OCACDBG(OCACLOG_VERBOSE, "duplicate setting, skip it!\n");
return -1;
}
if (ocac_params->traffic_ctrl && !ocac_info->qosnull_txdesc_host) {
/* Set qosnull frame */
ieee80211_ref_node(ni);
skb = ieee80211_get_qosnulldata(ni, WMM_AC_VO);
if (!skb) {
ieee80211_free_node(ni);
OCACDBG(OCACLOG_WARNING, "alloc skb error!\n");
ic->ic_ocac.ocac_counts.alloc_skb_error++;
return -1;
}
if (qdrv_ocac_set_frame(ic, ni, skb)) {
dev_kfree_skb_irq(skb);
ieee80211_free_node(ni);
OCACDBG(OCACLOG_WARNING, "set ocs frame error!\n");
ic->ic_ocac.ocac_counts.set_frame_error++;
return -1;
}
ieee80211_free_node(ni);
}
ocac_info->freq_band = qdrv_set_channel_freqband_setup(ic, chan);
ocac_info->off_channel = off_channel;
ocac_info->secure_dwell = ocac_params->secure_dwell_ms;
ocac_info->threshold_fat = ocac_params->thresh_fat;
ocac_info->threshold_traffic = ocac_params->thresh_traffic;
ocac_info->threshold_fat_dec = ocac_params->thresh_fat_dec;
ocac_info->traffic_ctrl = ocac_params->traffic_ctrl;
ocac_info->offset_txhalt = ocac_params->offset_txhalt;
ocac_info->offset_offchan = ocac_params->offset_offchan;
if (ieee80211_is_on_weather_channel(ic, chan))
ocac_info->dwell_time = ocac_params->wea_dwell_time_ms;
else
ocac_info->dwell_time = ocac_params->dwell_time_ms;
ic->ic_ocac.ocac_counts.set_run++;
if (ocac_params->traffic_ctrl)
ic->ic_update_ocac_state_ie(ic, OCAC_STATE_ONGOING, 0);
} else {
/* stop ocac in MuC */
if (ocac_info->off_channel == 0) {
OCACDBG(OCACLOG_VERBOSE, "duplicate setting, skip it!\n");
ic->ic_ocac.ocac_counts.skip_set_pend++;
return -1;
}
ocac_info->off_channel = 0;
ic->ic_ocac.ocac_counts.set_pend++;
if (ocac_params->traffic_ctrl)
ic->ic_update_ocac_state_ie(ic, OCAC_STATE_NONE, 0);
}
if (qdrv_hostlink_set_ocac(qw, sp->ocac_bus) < 0) {
ic->ic_ocac.ocac_counts.hostlink_err++;
OCACDBG(OCACLOG_WARNING, "hostlink set seamless dfs error!\n");
return -1;
}
ic->ic_ocac.ocac_counts.hostlink_ok++;
OCACDBG(OCACLOG_VERBOSE, "hostlink set seamless dfs succeed. off-chan: %u\n",
chan ? chan->ic_ieee : 0);
return 0;
}
int qtn_do_measurement(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_meas_chan_info *meas_info = sp->chan_meas_lhost;
struct ieee80211_global_measure_info *ic_meas_info = &ic->ic_measure_info;
int ret;