blob: 5780557608cbde7fe7be76fa40a947e7ae88b758 [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 "../../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;
meas_info->meas_reason = 0;
meas_info->work_channel = qdrv_set_channel_setup(ic, ic->ic_curchan);
switch (ic_meas_info->type) {
case IEEE80211_CCA_MEASTYPE_BASIC:
{
struct ieee80211_channel *meas_channel;
u_int8_t ieee_ch;
/* channel 0 means current channel */
if (ic_meas_info->param.basic.channel == 0)
ieee_ch = ic->ic_curchan->ic_ieee;
else
ieee_ch = ic_meas_info->param.basic.channel;
meas_channel = findchannel(ic, ieee_ch, ic->ic_des_mode);
if (NULL == meas_channel) {
return -EINVAL;
}
if (ic_meas_info->param.basic.duration_tu == 0) {
return -EINVAL;
}
meas_info->meas_type = QTN_MEAS_TYPE_BASIC;
meas_info->meas_channel = qdrv_set_channel_setup(ic, meas_channel);
meas_info->meas_dur_ms = IEEE80211_TU_TO_MS(ic_meas_info->param.basic.duration_tu);
meas_info->meas_start_tsf = ic_meas_info->param.basic.tsf;
meas_info->inter_data.basic_radar_num = ic->ic_radar_detections_num(ieee_ch);
break;
}
case IEEE80211_CCA_MEASTYPE_CCA:
{
struct ieee80211_channel *meas_channel;
int ieee_ch;
if (ic_meas_info->param.cca.channel == 0)
ieee_ch = ic->ic_curchan->ic_ieee;
else
ieee_ch = ic_meas_info->param.cca.channel;
meas_channel = findchannel(ic, ieee_ch, 0);
if (NULL == meas_channel)
return -EINVAL;
if (ic_meas_info->param.cca.duration_tu == 0)
return -EINVAL;
meas_info->meas_type = QTN_MEAS_TYPE_CCA;
meas_info->meas_channel = qdrv_set_channel_setup(ic, meas_channel);
meas_info->meas_dur_ms = IEEE80211_TU_TO_MS(ic_meas_info->param.cca.duration_tu);
meas_info->meas_start_tsf = ic_meas_info->param.cca.tsf;
break;
}
case IEEE80211_CCA_MEASTYPE_RPI:
{
struct ieee80211_channel *meas_channel;
int ieee_ch;
if (ic_meas_info->param.rpi.channel == 0)
ieee_ch = ic->ic_curchan->ic_ieee;
else
ieee_ch = ic_meas_info->param.rpi.channel;
meas_channel = findchannel(ic, ieee_ch, 0);
if (NULL == meas_channel)
return -EINVAL;
if (ic_meas_info->param.rpi.duration_tu == 0)
return -EINVAL;
meas_info->meas_type = QTN_MEAS_TYPE_RPI;
meas_info->meas_channel = qdrv_set_channel_setup(ic, meas_channel);
meas_info->meas_dur_ms = IEEE80211_TU_TO_MS(ic_meas_info->param.rpi.duration_tu);
meas_info->meas_start_tsf = ic_meas_info->param.rpi.tsf;
break;
}
case IEEE80211_RM_MEASTYPE_CH_LOAD:
{
struct ieee80211_channel *meas_channel;
int ieee_ch;
if (ic_meas_info->param.chan_load.op_class == 0 &&
ic_meas_info->param.chan_load.channel == 0)
ieee_ch = ic->ic_curchan->ic_ieee;
else
ieee_ch = ic_meas_info->param.chan_load.channel;
meas_channel = findchannel(ic, ieee_ch, 0);
if (NULL == meas_channel)
return -EINVAL;
if (ic_meas_info->param.chan_load.duration_tu == 0)
return -EINVAL;
meas_info->meas_type = QTN_MEAS_TYPE_CHAN_LOAD;
meas_info->meas_channel = qdrv_set_channel_setup(ic, meas_channel);
meas_info->meas_dur_ms = IEEE80211_TU_TO_MS(ic_meas_info->param.chan_load.duration_tu);
meas_info->meas_start_tsf = 0;
break;
}
case IEEE80211_RM_MEASTYPE_NOISE:
{
struct ieee80211_channel *meas_channel;
int ieee_ch;
if (ic_meas_info->param.noise_his.op_class == 0 &&
ic_meas_info->param.noise_his.channel == 0)
ieee_ch = ic->ic_curchan->ic_ieee;
else
ieee_ch = ic_meas_info->param.noise_his.channel;
meas_channel = findchannel(ic, ieee_ch, 0);
if (NULL == meas_channel)
return -EINVAL;
if (ic_meas_info->param.noise_his.duration_tu == 0)
return -EINVAL;
meas_info->meas_type = QTN_MEAS_TYPE_NOISE_HIS;
meas_info->meas_channel = qdrv_set_channel_setup(ic, meas_channel);
meas_info->meas_dur_ms = IEEE80211_TU_TO_MS(ic_meas_info->param.noise_his.duration_tu);
meas_info->meas_start_tsf = 0;
break;
}
default:
DBGPRINTF(DBG_LL_ALL, QDRV_LF_WLAN,
"[%s]unsupported measurement type\n",
__func__);
return -EINVAL;
}
ic_meas_info->status = MEAS_STATUS_RUNNING;
ret = qdrv_hostlink_meas_chan(qw, sp->chan_meas_bus);
if (ret & QTN_HLINK_RC_ERR) {
printk("measurement fail:%s\n",
meas_err_msg[meas_info->meas_reason - QTN_MEAS_REASON_OFF_CHANNEL_UNSUPPORT]);
ic_meas_info->status = MEAS_STATUS_IDLE;
return -EFAULT;
}
return 0;
}
EXPORT_SYMBOL(qtn_do_measurement);
static int qdrv_remain_on_channel(struct ieee80211com *ic,
struct ieee80211_node *ni, struct ieee80211_channel *off_chan,
int bandwidth, uint64_t start_tsf, uint32_t timeout, uint32_t duration, int flags)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct qtn_remain_chan_info *remain_info = sp->remain_chan_lhost;
if (!ic || !off_chan)
goto error;
if ((bandwidth < BW_HT20) || (bandwidth > BW_HT160))
goto error;
if ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "REMAIN CHANNEL %s:"
" Don't switch to off channel - scan in progress\n", __func__);
goto error;
}
if (!qdrv_radar_can_sample_chan()) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "REMAIN CHANNEL %s:"
" Don't switch to off channel - radar\n", __func__);
goto error;
}
/* 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)) {
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "REMAIN CHANNEL %s:"
" Don't switch to off channel - CoC idle\n", __func__);
goto error;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "remain on channel %u %dus\n",
off_chan->ic_ieee, duration);
remain_info->start_tsf = start_tsf;
remain_info->timeout_usecs = timeout;
remain_info->duration_usecs = duration;
remain_info->freq_band = qdrv_set_channel_freqband_setup(ic, ic->ic_bsschan);
remain_info->data_channel = qdrv_set_channel_setup(ic, ic->ic_bsschan);
memcpy(remain_info->peer_mac, ni->ni_macaddr, IEEE80211_ADDR_LEN);
off_chan->ic_ext_flags |= IEEE80211_CHAN_TDLS_OFF_CHAN;
if (bandwidth == BW_HT20) {
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_20;
remain_info->off_channel = qdrv_set_channel_setup(ic, off_chan);
ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_20;
} else if (bandwidth == BW_HT40) {
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_40;
remain_info->off_channel = qdrv_set_channel_setup(ic, off_chan);
ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_40;
} else {
remain_info->off_channel = qdrv_set_channel_setup(ic, off_chan);
}
off_chan->ic_ext_flags &= ~IEEE80211_CHAN_TDLS_OFF_CHAN;
remain_info->status = QTN_REM_CHAN_STATUS_HOST_IOCTL_SENT;
if (qdrv_hostlink_remain_chan(qw, sp->remain_chan_bus) < 0) {
remain_info->status = QTN_REM_CHAN_STATUS_IDLE;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "REMAIN CHANNEL %s:"
" hostlink set remain channel error!\n", __func__);
goto error;
}
#if defined(QBMPS_ENABLE)
/* indicate sample channel is on-going */
ic->ic_flags_qtn |= IEEE80211_QTN_SAMP_CHAN;
#endif
ic->ic_chan_switch_reason_record(ic, IEEE80211_CSW_REASON_TDLS_CS);
return 0;
error:
return -1;
}
static void
qdrv_use_rts_cts(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qdrv_hostlink_use_rtscts(qw, ic->ic_local_rts);
}
#if QTN_ENABLE_TRACE_BUFFER
static void
qdrv_dump_trace_buffer(struct shared_params *sp)
{
if (sp->p_debug_1 && sp->p_debug_2) {
int i;
/* NOTE: the number of records cannot dynamically change or buffer overflow may happen */
int records = sp->debug_1_arg;
static struct qtn_trace_record *p_local = NULL;
u_int32_t index = *((u_int32_t *)(muc_to_lhost((int)sp->p_debug_2))) + 1;
struct qtn_trace_record *p_first = (struct qtn_trace_record *)(muc_to_lhost)((int)sp->p_debug_1);
struct qtn_trace_record *p_entry = p_first + index;
struct qtn_trace_record *p_past = p_first + records;
if (records) {
size_t alloc_size = records * sizeof(struct qtn_trace_record);
if (p_local == NULL) {
p_local = kmalloc(alloc_size, GFP_KERNEL);
}
if (p_local) {
memcpy((void *)p_local, (void *)muc_to_lhost((int)sp->p_debug_1), alloc_size);
/*
* Re-cache the index - it may have changed by this point, if alloc above
* takes a while.
*/
index = *((u_int32_t *)(muc_to_lhost((int)sp->p_debug_2))) + 1;
p_first = p_local;
p_entry = p_first + index;
p_past = p_first + records;
}
}
if (index >= records) {
index = 0;
}
printk("Trace buffer for %d records %p %p %d:\n", records, p_first,
(u_int32_t *)(muc_to_lhost((int)sp->p_debug_2)), index);
for (i = 0; i < records; i++) {
printk(" T:0x%08X,E:0x%08X,D:0x%08X", p_entry->tsf, p_entry->event, p_entry->data);
if (i && (((i + 1) % 2) == 0)) {
printk("\n");
}
p_entry++;
if (p_entry >= p_past) {
p_entry = p_first;
}
}
}
}
static void qdrv_sleep_ms(int ms)
{
mdelay(ms);
}
#endif
void qdrv_muc_traceback(int force)
{
#if QTN_ENABLE_TRACE_BUFFER
int i;
struct net_device *dev;
for (i = 1; ; i++) {
dev = dev_get_by_index(&init_net, i);
if (dev == NULL) {
panic("Can't find a network device\n");
}
if (strncmp(dev->name, "wifi", 4) == 0) {
break;
} else {
dev_put(dev);
}
}
if (dev) {
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_mac *mac = qw->mac;
if (!mac->dead || force) {
qdrv_dump_trace_buffer(sp);
}
dev_put(dev);
}
#endif
}
EXPORT_SYMBOL(qdrv_muc_traceback);
void qdrv_halt_muc(void)
{
#if QTN_ENABLE_TRACE_BUFFER
int i;
struct net_device *dev;
for (i = 1; ; i++) {
dev = dev_get_by_index(&init_net, i);
if (dev == NULL) {
panic("Can't find a network device\n");
}
if (strncmp(dev->name, "wifi", 4) == 0) {
break;
} else {
dev_put(dev);
}
}
if (dev) {
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_mac *mac = qw->mac;
if (!mac->dead) {
printk("Interrupting MuC to halt (MAC:%p)\n", mac);
mac->dead = 1;
qdrv_mac_interrupt_muc_high(mac);
qtn_sleep_ms(100);
qdrv_dump_trace_buffer(sp);
} else {
printk("MAC already dead, not triggering another trace\n");
}
dev_put(dev);
}
#endif
}
EXPORT_SYMBOL(qdrv_halt_muc);
static void qtn_set_coverageclass(struct ieee80211com *ic)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
}
static u_int qtn_mhz2ieee(struct ieee80211com *ic, u_int freq, u_int flags)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
static struct ieee80211vap *qtn_vap_create(struct ieee80211com *ic,
const char *name, int unit, int opmode, int flags, struct net_device *dev)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return NULL;
}
static void qtn_vap_delete(struct ieee80211vap *iv)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return;
}
static uint8_t qdrv_get_vap_idx(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
return qv->qv_vap_idx;
}
static void qdrv_wlan_80211_updateslot(struct ieee80211com *ic)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return;
}
static int qdrv_wlan_80211_start(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qdrv_mac *mac = qw->mac;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Just in case re-enable rx interrupts */
qdrv_mac_enable_irq(mac, qw->rxirq);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
static int qdrv_wlan_80211_reset(struct ieee80211com *ic)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
static int qdrv_allow_report_frames_in_cac(struct ieee80211com *ic, struct ieee80211_node *ni,
const int user_conf_switch, bool *allow_rpt_frm)
{
if (NULL == allow_rpt_frm) {
ieee80211_free_node(ni);
return 0;
}
if (user_conf_switch) {
if (*allow_rpt_frm) {
*allow_rpt_frm = false;
} else if ((ieee80211_is_chan_radar_detected(ic->ic_curchan)) ||
(ieee80211_is_chan_cac_required(ic->ic_curchan))) {
ieee80211_free_node(ni);
return 0;
}
}
return 1;
}
/*
* Transmit an 802.11 encapped management or data frame via the management path.
* This function must be called with a reference to the node structure.
*/
static int qdrv_wlan_80211_send(struct ieee80211com *ic, struct ieee80211_node *ni,
struct sk_buff *skb, uint32_t priority, uint8_t is_mgmt)
{
struct net_device *vdev;
struct qdrv_vap *qv;
if (!qdrv_allow_report_frames_in_cac(ic, ni, ic->sta_dfs_info.sta_dfs_strict_mode,
&ic->sta_dfs_info.allow_measurement_report)) {
return 0;
}
#ifdef CONFIG_QHOP
if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_RBS) {
if (!qdrv_allow_report_frames_in_cac(ic, ni, ic->rbs_mbs_dfs_info.rbs_mbs_allow_tx_frms_in_cac,
&ic->rbs_mbs_dfs_info.rbs_allow_qhop_report)) {
return 0;
}
}
if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_MBS) {
if (!qdrv_allow_report_frames_in_cac(ic, ni, ic->rbs_mbs_dfs_info.rbs_mbs_allow_tx_frms_in_cac,
&ic->rbs_mbs_dfs_info.mbs_allow_csa)) {
return 0;
}
}
#endif
qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
vdev = qv->ndev;
skb->dev = vdev;
skb->dest_port = ni->ni_node_idx;
skb->priority = priority;
QTN_SKB_CB_NI(skb) = ni;
if (is_mgmt)
QTN_SKB_ENCAP(skb) = QTN_SKB_ENCAP_80211_MGMT;
else
QTN_SKB_ENCAP(skb) = QTN_SKB_ENCAP_80211_DATA;
M_FLAG_SET(skb, M_NO_AMSDU);
dev_queue_xmit(skb);
ieee80211_free_node(ni);
return 0;
}
static void qdrv_wlan_80211_disassoc(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = vap->iv_ic;
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct qdrv_node *qn = container_of(ni, struct qdrv_node, qn_node);
struct qtn_vlan_dev *vdev = vdev_tbl_lhost[QDRV_WLANID_FROM_DEVID(qv->devid)];
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE,
"Node %02x:%02x:%02x:%02x:%02x:%02x %s for BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
DBGMACFMT(ni->ni_macaddr),
"dissociated",
DBGMACFMT(ni->ni_bssid));
if (ic->ic_wowlan.host_state) {
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,
"%s WoWLAN: Wake up host\n", __func__);
wowlan_wakeup_host();
}
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate DISASSOC message\n");
vnet_free_ioctl(ioctl);
return;
}
if (ic->sta_dfs_info.sta_dfs_strict_mode) {
if (ieee80211_is_chan_available(ic->ic_bsschan)) {
ic->ic_mark_channel_availability_status(ic, ic->ic_bsschan,
IEEE80211_CHANNEL_STATUS_NON_AVAILABLE);
}
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memset(args, 0, sizeof(*args));
memcpy(args->ni_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN);
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->ni_associd = IEEE80211_NODE_AID(ni);
ioctl->ioctl_command = IOCTL_DEV_DISASSOC;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
if (qn->qn_node_idx)
switch_vlan_clr_node(vdev, qn->qn_node_idx);
}
static void qdrv_wlan_qtnie_parse(struct ieee80211_node *ni, struct ieee80211com *ic,
struct qtn_node_args *args)
{
struct ieee80211_ie_qtn *qtnie = (struct ieee80211_ie_qtn *)ni->ni_qtn_assoc_ie;
if (IEEE80211_QTN_TYPE_ENVY(qtnie)) {
args->ni_qtn_ie_flags = qtnie->qtn_ie_flags;
} else {
args->ni_qtn_ie_flags = qtnie->qtn_ie_my_flags;
}
if (IEEE80211_QTN_IE_GE_V5(qtnie)) {
args->ni_ver_sw = min(ni->ni_ver_sw, ic->ic_ver_sw);
args->ni_rate_train = ni->ni_rate_train;
args->ni_rate_train_peer = ntohl(get_unaligned(&qtnie->qtn_ie_rate_train));
}
}
static void qdrv_wlan_80211_newassoc(struct ieee80211_node *ni, int isnew)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct qdrv_node *qn = container_of(ni, struct qdrv_node, qn_node);
struct ieee80211com *ic = ni->ni_vap->iv_ic;
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
struct qtn_vlan_dev *vdev = vdev_tbl_lhost[QDRV_WLANID_FROM_DEVID(qv->devid)];
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE,
"Node %02x:%02x:%02x:%02x:%02x:%02x %s for BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
DBGMACFMT(ni->ni_macaddr),
isnew ? "associated" : "reassociated",
DBGMACFMT(ni->ni_bssid));
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate NEWASSOC message\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(2)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memset(ni->ni_shared_stats, 0, sizeof(*ni->ni_shared_stats));
args->ni_node_idx = 0;
args->ni_shared_stats = ni->ni_shared_stats_phys;
memcpy(args->ni_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN);
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->ni_raw_bintval = ni->ni_raw_bintval;
args->ni_associd = IEEE80211_NODE_AID(ni);
args->ni_flags = ni->ni_flags;
args->ni_qtn_flags = ni->ni_qtn_flags;
args->ni_tdls_status = ni->tdls_status;
args->ni_vendor = ni->ni_vendor;
args->ni_bbf_disallowed = ni->ni_bbf_disallowed;
args->ni_std_bf_disallowed = ni->ni_std_bf_disallowed;
args->ni_uapsd = ni->ni_uapsd;
memcpy(args->ni_rates, ni->ni_rates.rs_rates, IEEE80211_RATE_MAXSIZE);
memcpy(args->ni_htrates, ni->ni_htrates.rs_rates, IEEE80211_HT_RATE_MAXSIZE);
args->ni_nrates = ni->ni_rates.rs_nrates;
args->ni_htnrates = ni->ni_htrates.rs_nrates;
memcpy(args->ni_htcap, &ni->ni_htcap,
sizeof(args->ni_htcap));
memcpy(args->ni_htinfo, &ni->ni_htinfo,
sizeof(args->ni_htinfo));
if (IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) {
memcpy(args->ni_vhtcap, &ni->ni_vhtcap,
sizeof(args->ni_vhtcap));
memcpy(args->ni_vhtop, &ni->ni_vhtop,
sizeof(args->ni_vhtop));
memcpy(args->ni_mu_grp, &ni->ni_mu_grp,
sizeof(args->ni_mu_grp));
}
args->ni_rsn_caps = ni->ni_rsn.rsn_caps;
args->rsn_ucastcipher = ni->ni_rsn.rsn_ucastcipher;
args->tdls_peer_associd = ni->tdls_peer_associd;
/* Automatic install of BA */
if (ni->ni_implicit_ba_valid) {
args->ni_implicit_ba_rx = ni->ni_implicit_ba;
args->ni_implicit_ba_tx = ni->ni_vap->iv_implicit_ba;
args->ni_implicit_ba_size = ni->ni_implicit_ba_size;
}
if (ni->ni_qtn_assoc_ie)
qdrv_wlan_qtnie_parse(ni, ic, args);
ioctl->ioctl_command = IOCTL_DEV_NEWASSOC;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = isnew;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
if (args->ni_node_idx == 0) {
DBGPRINTF_E("[%pM] node alloc failed\n", ni->ni_macaddr);
} else {
uint32_t ni_node_idx = ni->ni_node_idx;
ieee80211_idx_add(ni, args->ni_node_idx);
if (((ni->ni_vap->iv_opmode == IEEE80211_M_WDS)
&& (ni_node_idx != 0))
|| (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP
&& ni_node_idx != ni->ni_vap->iv_bss->ni_node_idx)) {
qdrv_remove_invalid_sub_port(ni->ni_vap, ni_node_idx);
}
if (qv->iv.iv_opmode == IEEE80211_M_HOSTAP
&& QVLAN_IS_DYNAMIC(vdev)) {
qn->qn_node_idx = args->ni_node_idx;
switch_vlan_set_node(vdev, args->ni_node_idx, QVLAN_DEF_PVID); /* By default put a STA in VLAN 1 */
}
}
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_80211_node_update(struct ieee80211_node *ni)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE,
"Node "DBGMACVAR" updated for BSSID "DBGMACVAR"\n",
DBGMACFMT(ni->ni_macaddr),
DBGMACFMT(ni->ni_bssid));
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate NODE_UPDATE message\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(2)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->ni_qtn_flags = ni->ni_qtn_flags;
args->ni_vendor = ni->ni_vendor;
args->ni_bbf_disallowed = ni->ni_bbf_disallowed;
ioctl->ioctl_command = IOCTL_DEV_NODE_UPDATE;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = 0;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_register_node(struct ieee80211_node *ni)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct qtn_vlan_dev *vdev = vdev_tbl_lhost[QDRV_WLANID_FROM_DEVID(qv->devid)];
fwt_sw_register_node(ni->ni_node_idx);
switch_vlan_register_node(IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), vdev);
}
static void qdrv_wlan_unregister_node(struct ieee80211_node *ni)
{
fwt_sw_unregister_node(ni->ni_node_idx);
switch_vlan_unregister_node(IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx));
}
static void qdrv_wlan_80211_resetmaxqueue(struct ieee80211_node *ni)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
if ((ioctl = vnet_alloc_ioctl(qv)) == NULL)
return;
if ((args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC)) == NULL) {
DBGPRINTF_E("Failed to allocate message for resetting queue\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
memcpy(args->ni_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN);
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->ni_associd = IEEE80211_NODE_AID(ni);
args->ni_node_idx = ni->ni_node_idx;
ioctl->ioctl_command = IOCTL_DEV_RST_QUEUE_DEPTH;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
/*
* Process setkey request
*/
static void qdrv_wlan_80211_setkey(struct ieee80211vap *vap,
const struct ieee80211_key *k, const u_int8_t mac[IEEE80211_ADDR_LEN])
{
struct qtn_key_args *args = NULL;
dma_addr_t args_dma;
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate SETKEY message\n");
vnet_free_ioctl(ioctl);
return;
}
/* Copy the values over */
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(3)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memcpy((u_int8_t*)&args->key, (u_int8_t*)k, sizeof(struct qtn_key));
memcpy(args->wk_addr, mac, IEEE80211_ADDR_LEN);
ioctl->ioctl_command = IOCTL_DEV_SETKEY;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
/*
* Process delkey request
*/
static void qdrv_wlan_80211_delkey(struct ieee80211vap *vap,
const struct ieee80211_key *k, const u_int8_t mac[IEEE80211_ADDR_LEN])
{
struct qtn_key_args *args = NULL;
dma_addr_t args_dma;
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E( "Failed to allocate DELKEY message\n");
vnet_free_ioctl(ioctl);
return;
}
/* Copy the values over */
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(4)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memcpy((u_int8_t*)&args->key, (u_int8_t*)k, sizeof(struct qtn_key));
if (mac) {
memcpy(args->wk_addr, mac, IEEE80211_ADDR_LEN);
}
ioctl->ioctl_command = IOCTL_DEV_DELKEY;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
/*
* Process addba request
*/
static void qdrv_wlan_80211_process_addba(struct ieee80211_node *ni, int tid,
int direction)
{
struct qtn_baparams_args *args = NULL;
dma_addr_t args_dma;
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
if (tid >= WME_NUM_TID) {
return;
}
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate ADDBA message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(5)ioctl %p dma ptr %p\n", ioctl, (void *)args);
args->tid = tid;
memcpy(args->ni_addr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->type = IEEE80211_BA_IMMEDIATE;
if (direction) {
args->state = ni->ni_ba_tx[tid].state;
args->start_seq_num = ni->ni_ba_tx[tid].seq;
args->window_size = ni->ni_ba_tx[tid].buff_size;
args->lifetime = ni->ni_ba_tx[tid].timeout;
args->flags = ni->ni_ba_tx[tid].flags;
} else {
args->state = ni->ni_ba_rx[tid].state;
args->start_seq_num = ni->ni_ba_rx[tid].seq;
args->window_size = ni->ni_ba_rx[tid].buff_size;
args->lifetime = ni->ni_ba_rx[tid].timeout;
args->flags = ni->ni_ba_rx[tid].flags;
}
if (direction) {
ioctl->ioctl_command = IOCTL_DEV_BA_ADDED_TX;
} else {
ioctl->ioctl_command = IOCTL_DEV_BA_ADDED_RX;
}
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
/*
* Send addba request
*/
static void qdrv_wlan_80211_send_addba(struct ieee80211_node *ni, int tid)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_action_data act;
struct ba_action_req ba_req;
memset(&act, 0, sizeof(act));
memset(&ba_req, 0, sizeof(ba_req));
act.cat = IEEE80211_ACTION_CAT_BA;
act.action = IEEE80211_ACTION_BA_ADDBA_REQ;
ba_req.tid = tid;
ba_req.frag = 0;
ba_req.type = IEEE80211_BA_IMMEDIATE;
ba_req.seq = ni->ni_ba_tx[tid].seq;
ba_req.buff_size = ni->ni_ba_tx[tid].buff_size;
ba_req.timeout = ni->ni_ba_tx[tid].timeout;
act.params = (void *)&ba_req;
ic->ic_send_mgmt(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&act);
}
static void qdrv_wlan_update_wmm_params(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
struct host_ioctl *ioctl;
int i;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate WMM params message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(6)ioctl %p dma ptr %p\n", ioctl, (void *)args);
for (i = 0; i < WME_NUM_AC; i++) {
args->wmm_params[i] = wme->wme_chanParams.cap_wmeParams[i];
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s - %x Send new WMM parameters\n",
vap->iv_dev->name, ioctl->ioctl_argp);
ioctl->ioctl_command = IOCTL_DEV_WMM_PARAMS;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = 1;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_update_chan_power_table(struct ieee80211vap *vap,
struct ieee80211_channel *chan)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct ieee80211_chan_power_table *args = NULL;
dma_addr_t args_dma;
struct host_ioctl *ioctl;
int8_t *s_pwrs;
int8_t *d_pwrs;
int idx_bf;
int idx_ss;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate ioctl message for channel power table\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(6)ioctl %p dma ptr %p\n", ioctl, (void *)args);
args->chan_ieee = chan->ic_ieee;
for (idx_bf = PWR_IDX_BF_OFF; idx_bf < PWR_IDX_BF_MAX; idx_bf++) {
for (idx_ss = PWR_IDX_1SS; idx_ss < PWR_IDX_SS_MAX; idx_ss++) {
s_pwrs = chan->ic_maxpower_table[idx_bf][idx_ss];
d_pwrs = args->maxpower_table[idx_bf][idx_ss];
d_pwrs[PWR_IDX_20M] = s_pwrs[PWR_IDX_20M];
d_pwrs[PWR_IDX_40M] = s_pwrs[PWR_IDX_40M];
d_pwrs[PWR_IDX_80M] = s_pwrs[PWR_IDX_80M];
}
}
ioctl->ioctl_command = IOCTL_DEV_SET_CHAN_POWER_TABLE;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = 1;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_bbsort_prio(struct ieee80211_wme_state *wme)
{
int i, j, temp_aifsn, temp_band;
for (i = 1; i < QDRV_SCH_BANDS - 1; i++) {
for (j = 1; j < QDRV_SCH_BANDS - i; j++) {
if (qdrv_sch_band_chg_prio[j].aifsn > qdrv_sch_band_chg_prio[j + 1].aifsn) {
temp_aifsn = qdrv_sch_band_chg_prio[j].aifsn;
temp_band = qdrv_sch_band_chg_prio[j].band_prio;
qdrv_sch_band_chg_prio[j].aifsn = qdrv_sch_band_chg_prio[j + 1].aifsn;
qdrv_sch_band_chg_prio[j].band_prio = qdrv_sch_band_chg_prio[j + 1].band_prio;
qdrv_sch_band_chg_prio[j + 1].aifsn = temp_aifsn;
qdrv_sch_band_chg_prio[j + 1].band_prio = temp_band;
}
}
}
}
static void qdrv_wlan_80211_join_bss(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct ieee80211_node *ni = vap->iv_bss;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
struct host_ioctl *ioctl;
int i;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate JOIN BSS message\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(6)ioctl %p dma ptr %p\n", ioctl, (void *)args);
for (i = 0; i < WME_NUM_AC; i++) {
args->wmm_params[i] = wme->wme_chanParams.cap_wmeParams[i];
/* This is to inform qdrv scheduler that better AC may have worse parameter settings */
qdrv_sch_band_chg_prio[i].band_prio = qdrv_sch_band_prio[i];
qdrv_sch_band_chg_prio[i + 1].aifsn =
wme->wme_chanParams.cap_wmeParams[qdrv_sch_band_prio[i + 1]].wmm_aifsn;
}
qdrv_sch_band_chg_prio[i].band_prio = qdrv_sch_band_prio[i];
qdrv_sch_band_chg_prio[0].aifsn = wme->wme_chanParams.cap_wmeParams[3].wmm_aifsn;
qdrv_wlan_bbsort_prio(wme);
memset(ni->ni_shared_stats, 0, sizeof(*ni->ni_shared_stats));
args->ni_shared_stats = ni->ni_shared_stats_phys;
memcpy(args->ni_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN);
memcpy(args->ni_macaddr, vap->iv_myaddr, IEEE80211_ADDR_LEN);
args->ni_associd = IEEE80211_NODE_AID(ni);
args->ni_flags = ni->ni_flags;
args->ni_vendor = ni->ni_vendor;
memcpy(args->ni_rates, ni->ni_rates.rs_rates, IEEE80211_RATE_MAXSIZE);
memcpy(args->ni_htrates, ni->ni_htrates.rs_rates, IEEE80211_HT_RATE_MAXSIZE);
args->ni_nrates = ni->ni_rates.rs_nrates;
args->ni_htnrates = ni->ni_htrates.rs_nrates;
memcpy(args->ni_htcap, &ni->ni_htcap, sizeof(struct ieee80211_htcap));
memcpy(args->ni_htinfo, &ni->ni_htinfo, sizeof(struct ieee80211_htinfo));
if (IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) {
memcpy(args->ni_vhtcap, &ni->ni_vhtcap,
sizeof(args->ni_vhtcap));
memcpy(args->ni_vhtop, &ni->ni_vhtop,
sizeof(args->ni_vhtop));
}
if (ni->ni_rsn_ie != NULL) {
args->ni_rsn_caps = ni->ni_rsn.rsn_caps;
}
args->rsn_ucastcipher = ni->ni_rsn.rsn_ucastcipher;
/* Automatic install of BA */
if (ni->ni_implicit_ba_valid) {
args->ni_implicit_ba_rx = ni->ni_implicit_ba;
args->ni_implicit_ba_tx = ni->ni_vap->iv_implicit_ba;
args->ni_implicit_ba_size = ni->ni_implicit_ba_size;
} else {
args->ni_implicit_ba_rx = 0;
args->ni_implicit_ba_tx = 0;
}
if (ni->ni_qtn_assoc_ie) {
qdrv_wlan_qtnie_parse(ni, ic, args);
g_qdrv_non_qtn_assoc = 0;
} else {
g_qdrv_non_qtn_assoc = 1;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN, "%s - %x Send new BSSID\n",
vap->iv_dev->name, ioctl->ioctl_argp);
ioctl->ioctl_command = IOCTL_DEV_NEWBSSID;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = 1;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
ieee80211_idx_add(ni, args->ni_node_idx);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
#if defined(QBMPS_ENABLE)
if (ic->ic_flags_qtn & IEEE80211_QTN_BMPS) {
/* allocate or free/re-allocate null frame */
ieee80211_sta_bmps_update(vap);
}
#endif
}
static void qdrv_wlan_80211_beacon_update(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct ieee80211_node *ni = vap->iv_bss;
struct qtn_beacon_args *bc_args = NULL;
dma_addr_t args_dma;
struct host_ioctl *ioctl;
struct sk_buff *beacon_skb;
int i;
if (!(vap->iv_dev->flags & IFF_RUNNING) || ic->ic_bsschan == IEEE80211_CHAN_ANYC)
return;
if (vap->iv_opmode == IEEE80211_M_WDS)
return;
spin_lock(&qv->bc_lock);
if (ieee80211_beacon_create_param(vap) != 0) {
spin_unlock(&qv->bc_lock);
return;
}
memset(&qv->qv_boff, 0, sizeof(qv->qv_boff));
beacon_skb = ieee80211_beacon_alloc(ni, &qv->qv_boff);
if (beacon_skb == NULL) {
spin_unlock(&qv->bc_lock);
return;
}
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(bc_args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*bc_args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate BEACON UPDATE message\n");
dev_kfree_skb_any(beacon_skb);
vnet_free_ioctl(ioctl);
ieee80211_beacon_destroy_param(vap);
spin_unlock(&qv->bc_lock);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(7)ioctl %p dma ptr %p\n", ioctl, (void *)bc_args);
ieee80211_beacon_update(ni, &qv->qv_boff, beacon_skb, 0);
for (i = 0; i < WME_NUM_AC; i++) {
bc_args->wmm_params[i] = wme->wme_chanParams.cap_wmeParams[i];
}
bc_args->bo_tim_len = qv->qv_boff.bo_tim_len;
bc_args->bintval = ic->ic_lintval;
bc_args->bo_htcap = 0;
if (ic->ic_htinfo.choffset) {
/* Network is operating in 40 MHZ mode */
bc_args->bo_htinfo = 1;
} else {
/* Network is operating in 20 MHZ mode */
bc_args->bo_htinfo = 0;
}
/* This is an 11AC network */
if (IS_IEEE80211_VHT_ENABLED(ic)) {
bc_args->bo_vhtcap = 1;
bc_args->bo_vhtop = ic->ic_vhtop.chanwidth;
} else if (IS_IEEE80211_11NG_VHT_ENABLED(ic)) {
bc_args->bo_vhtcap = 1;
bc_args->bo_vhtop = ic->ic_vhtop_24g.chanwidth;
}
ieee80211_beacon_flush_param(vap->param);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
flush_dcache_sizerange_safe(beacon_skb->data, beacon_skb->len);
#else
flush_dcache_range((uint32_t)beacon_skb->data,
(uint32_t)beacon_skb->data + beacon_skb->len);
#endif
/* Convert to MuC mapping address before ioctl request */
bc_args->bc_ie_head = plat_kernel_addr_to_dma(NULL, vap->param->head);
bc_args->bc_ie_buf_start = plat_kernel_addr_to_dma(NULL, vap->param->buf);
ioctl->ioctl_command = IOCTL_DEV_BEACON_START;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = beacon_skb->len | (1 << 16);
ioctl->ioctl_argp = args_dma;
#ifdef LHOST_DEBUG_BEACON
printk("LHOST send a beacon %p length %d\n", beacon_skb->data, beacon_skb->len);
ieee80211_dump_beacon_desc_ie(vap->param);
#endif
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*bc_args), bc_args, args_dma);
/* Require MuC to receive and copy the list as well as the beacon buffer */
ieee80211_beacon_destroy_param(vap);
dev_kfree_skb_any(beacon_skb);
spin_unlock(&qv->bc_lock);
ic->ic_init(ic);
}
static void qdrv_wlan_80211_beacon_stop(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
struct host_ioctl *ioctl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate BEACON STOP message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(11)ioctl %p dma ptr %p\n", ioctl, (void *)args);
ioctl->ioctl_command = IOCTL_DEV_BEACON_STOP;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
/*
* Process delba request
*/
static void qdrv_wlan_80211_process_delba(struct ieee80211_node *ni, int tid,
int direction)
{
struct qtn_baparams_args *args = NULL;
dma_addr_t args_dma;
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate PROCESS_DELBA message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(8)ioctl %p dma ptr %p\n", ioctl, (void *)args);
/* Copy the values over */
if (direction) {
args->state = ni->ni_ba_tx[tid].state;
args->tid = tid;
args->type = IEEE80211_BA_IMMEDIATE;
args->start_seq_num = ni->ni_ba_tx[tid].seq;
args->window_size = ni->ni_ba_tx[tid].buff_size;
args->lifetime = ni->ni_ba_tx[tid].timeout;
} else {
args->tid = tid;
args->state = ni->ni_ba_rx[tid].state;
args->type = IEEE80211_BA_IMMEDIATE;
args->start_seq_num = ni->ni_ba_rx[tid].seq;
args->window_size = ni->ni_ba_rx[tid].buff_size;
args->lifetime = ni->ni_ba_rx[tid].timeout;
}
memcpy(args->ni_addr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
ioctl->ioctl_command = direction ? IOCTL_DEV_BA_REMOVED_TX : IOCTL_DEV_BA_REMOVED_RX;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_80211_tdls_operation(struct ieee80211_node *ni,
uint32_t ioctl_cmd, int cmd, uint32_t *value)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_tdls_args *args = NULL;
dma_addr_t args_dma;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate TDLS set message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)args);
memset(args, 0, sizeof(*args));
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
args->tdls_cmd = cmd;
args->ni_ncidx = ni->ni_node_idx;
args->tdls_params = *value;
ioctl->ioctl_command = ioctl_cmd;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
*value = args->tdls_params;
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_wlan_80211_tdls_set_params(struct ieee80211_node *ni, int cmd, int value)
{
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE, "Node %pM set tdls param %d to "
"%d-0x%x\n", ni->ni_macaddr, cmd, value, value);
qdrv_wlan_80211_tdls_operation(ni, IOCTL_DEV_SET_TDLS_PARAM, cmd, (uint32_t*)&value);
}
static uint32_t qdrv_wlan_80211_tdls_get_params(struct ieee80211_node *ni, int cmd)
{
uint32_t value = 0;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE, "Node %pM get tdls param %d\n",
ni->ni_macaddr, cmd);
qdrv_wlan_80211_tdls_operation(ni, IOCTL_DEV_GET_TDLS_PARAM, cmd, &value);
return value;
}
/*
* Enter/Leave power save state on STA mode
*/
static void qdrv_wlan_80211_power_save(struct ieee80211_node *ni, int enable)
{
struct qtn_power_save_args *args = NULL;
dma_addr_t args_dma;
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate POWER_SAVE message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(8)ioctl %p dma ptr %p\n", ioctl, (void *)args);
args->enable = !!enable;
memcpy(args->ni_addr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
ioctl->ioctl_command = IOCTL_DEV_POWER_SAVE;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
#ifndef ifr_media
#define ifr_media ifr_ifru.ifru_ivalue
#endif
static void qdrv_wlan_80211_set_cap_bw(struct ieee80211_node *ni, int bw)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
struct ifreq ifr;
int retv;
if ((bw > BW_HT40) && !ieee80211_swfeat_is_supported(SWFEAT_ID_VHT, 1))
return;
if (bw == qdrv_wlan_80211_get_cap_bw(ic))
return;
if (bw == BW_HT20) {
if (IS_IEEE80211_VHT_ENABLED(ic))
ic->ic_phymode = IEEE80211_MODE_11AC_VHT20PM;
else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
ic->ic_phymode = IEEE80211_MODE_11NA;
else if ((IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) &&
(ic->ic_phymode == IEEE80211_MODE_11NG_HT40PM))
ic->ic_phymode = IEEE80211_MODE_11NG;
} else if (bw == BW_HT40) {
if (IS_IEEE80211_VHT_ENABLED(ic))
ic->ic_phymode = IEEE80211_MODE_11AC_VHT40PM;
else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
ic->ic_phymode = IEEE80211_MODE_11NA_HT40PM;
else
ic->ic_phymode = IEEE80211_MODE_11NG_HT40PM;
} else if (bw == BW_HT80) {
if (IS_IEEE80211_VHT_ENABLED(ic)) {
ic->ic_phymode = IEEE80211_MODE_11AC_VHT80PM;
} else {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN,
"BW %d cannot be configured in current phymode\n", bw);
return;
}
} else {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "BW %d is not valid\n", bw);
return;
}
ieee80211_update_bw_capa(vap, bw);
memset(&ifr, 0, sizeof(ifr));
if(vap->iv_media.ifm_cur == NULL)
return;
ifr.ifr_media = vap->iv_media.ifm_cur->ifm_media &~ IFM_MMASK;
ifr.ifr_media |= IFM_MAKEMODE(ic->ic_phymode);
retv = ifmedia_ioctl(vap->iv_dev, &ifr, &vap->iv_media, SIOCSIFMEDIA);
if (retv == -ENETRESET) {
ic->ic_des_mode = ic->ic_phymode;
ieee80211_setmode(ic, ic->ic_des_mode);
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "QDRV: PHY mode %d\n", ic->ic_phymode);
}
static void qdrv_wlan_80211_set_cap_sgi(struct ieee80211_node *ni, int sgi)
{
struct ieee80211com *ic = ni->ni_ic;
if (sgi) {
ic->ic_vhtcap.cap_flags |= IEEE80211_VHTCAP_C_SHORT_GI_80;
ic->ic_vhtcap_24g.cap_flags |= IEEE80211_VHTCAP_C_SHORT_GI_80;
if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40)
ic->ic_htcap.cap |= (IEEE80211_HTCAP_C_SHORTGI40 |
IEEE80211_HTCAP_C_SHORTGI20);
else
ic->ic_htcap.cap |= IEEE80211_HTCAP_C_SHORTGI20;
} else {
ic->ic_vhtcap.cap_flags &= ~IEEE80211_VHTCAP_C_SHORT_GI_80;
ic->ic_vhtcap_24g.cap_flags &= ~IEEE80211_VHTCAP_C_SHORT_GI_80;
if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40)
ic->ic_htcap.cap &= ~(IEEE80211_HTCAP_C_SHORTGI40 |
IEEE80211_HTCAP_C_SHORTGI20);
else
ic->ic_htcap.cap &= ~IEEE80211_HTCAP_C_SHORTGI20;
}
}
static void qdrv_wlan_80211_set_ldpc(struct ieee80211_node *ni, int ldpc)
{
struct ieee80211com *ic = ni->ni_ic;
ic->ldpc_enabled = (ldpc & 0x1);
}
static void qdrv_wlan_80211_set_stbc(struct ieee80211_node *ni, int stbc)
{
struct ieee80211com *ic = ni->ni_ic;
ic->stbc_enabled = (stbc & 0x1);
}
static void qdrv_wlan_80211_set_rts_cts(struct ieee80211_node *ni, int rts_cts)
{
struct ieee80211com *ic = ni->ni_ic;
ic->rts_cts_prot = (rts_cts & 0x1);
}
static void qdrv_wlan_80211_set_peer_rts_mode(struct ieee80211_node *ni, int mode)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap;
uint8_t beacon_update_required = 0;
if (mode > IEEE80211_PEER_RTS_MAX) {
mode = IEEE80211_PEER_RTS_DEFAULT;
}
ic->ic_peer_rts_mode = mode;
if ((mode == IEEE80211_PEER_RTS_DYN) &&
(ic->ic_peer_rts != ic->ic_dyn_peer_rts)) {
ic->ic_peer_rts = ic->ic_dyn_peer_rts;
beacon_update_required = 1;
} else if (mode == IEEE80211_PEER_RTS_PMP) {
if ((ic->ic_sta_assoc - ic->ic_nonqtn_sta) >= IEEE80211_MAX_STA_CCA_ENABLED) {
if (ic->ic_peer_rts != 1) {
ic->ic_peer_rts = 1;
beacon_update_required = 1;
}
} else {
if (ic->ic_peer_rts != 0) {
ic->ic_peer_rts = 0;
beacon_update_required = 1;
}
}
} else if (mode == IEEE80211_PEER_RTS_OFF) {
ic->ic_peer_rts = 0;
beacon_update_required = 1;
}
if (beacon_update_required) {
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (vap->iv_state != IEEE80211_S_RUN)
continue;
ic->ic_beacon_update(vap);
}
}
}
static void qdrv_wlan_80211_set_11n40_only_mode(struct ieee80211_node *ni, int mode)
{
struct ieee80211com *ic = ni->ni_ic;
ic->ic_11n_40_only_mode = (mode & 0x1);
}
static void qdrv_wlan_80211_set_legacy_retry(struct ieee80211_node *ni, int retry_count)
{
struct ieee80211com *ic = ni->ni_ic;
ic->ic_legacy_retry_limit = (u_int8_t)retry_count;
}
static void qdrv_wlan_80211_set_retry_count(struct ieee80211_node *ni, int retry_count)
{
struct ieee80211com *ic = ni->ni_ic;
ic->ic_retry_count = (u_int8_t)retry_count;
}
static void qdrv_wlan_80211_set_mcsset(struct ieee80211com *ic)
{
memset(ic->ic_htcap.mcsset, 0, sizeof(ic->ic_htcap.mcsset));
if (ic->ic_ht_nss_cap == IEEE80211_HT_NSS1) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS1] = 0xff;
} else if (ic->ic_ht_nss_cap == IEEE80211_HT_NSS2) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS1] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS2] = 0xff;
if (ic->ic_caps & IEEE80211_C_UEQM) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1] =
IEEE80211_HT_MCSSET_20_40_UEQM1_2SS;
}
} else if (ic->ic_ht_nss_cap == IEEE80211_HT_NSS3) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS1] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS2] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS3] = 0xff;
if (ic->ic_caps & IEEE80211_C_UEQM) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1] =
IEEE80211_HT_MCSSET_20_40_UEQM1_2SS |
IEEE80211_HT_MCSSET_20_40_UEQM1_3SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM2] =
IEEE80211_HT_MCSSET_20_40_UEQM2_3SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM3] =
IEEE80211_HT_MCSSET_20_40_UEQM3_3SS;
}
} else if (ic->ic_ht_nss_cap == IEEE80211_HT_NSS4) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS1] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS2] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS3] = 0xff;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS4] = 0xff;
if (ic->ic_caps & IEEE80211_C_UEQM) {
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1] =
IEEE80211_HT_MCSSET_20_40_UEQM1_2SS |
IEEE80211_HT_MCSSET_20_40_UEQM1_3SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM2] =
IEEE80211_HT_MCSSET_20_40_UEQM2_3SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM3] =
IEEE80211_HT_MCSSET_20_40_UEQM3_3SS |
IEEE80211_HT_MCSSET_20_40_UEQM3_4SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM4] =
IEEE80211_HT_MCSSET_20_40_UEQM4_4SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM5] =
IEEE80211_HT_MCSSET_20_40_UEQM5_4SS;
ic->ic_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM6] =
IEEE80211_HT_MCSSET_20_40_UEQM6_4SS;
}
}
}
static void qdrv_wlan_80211_set_mcsparams(struct ieee80211com *ic)
{
ic->ic_htcap.mcsparams = IEEE80211_HTCAP_MCS_TX_SET_DEFINED;
ic->ic_htcap.numtxspstr = 0;
switch (ic->ic_ht_nss_cap) {
case IEEE80211_HT_NSS4:
if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) {
ic->ic_htcap.numtxspstr = IEEE80211_HTCAP_MCS_TWO_TX_SS;
ic->ic_htcap.mcsparams |= IEEE80211_HTCAP_MCS_TX_RX_SET_NEQ;
if (ic->ic_caps & IEEE80211_C_UEQM)
ic->ic_htcap.mcsparams |= IEEE80211_HTCAP_MCS_TX_UNEQ_MOD;
}
break;
default:
break;
}
}
static u_int16_t qdrv_wlan_80211_vhtmcs_map(enum ieee80211_vht_nss vhtnss,
enum ieee80211_vht_mcs_supported vhtmcs)
{
/* For Spatial stream from 1-8; set MCS=3 (not supported) */
u_int16_t vhtmcsmap = IEEE80211_VHTMCS_ALL_DISABLE;
switch(vhtnss) {
case IEEE80211_VHT_NSS8:
vhtmcsmap &= 0x3FFF;
vhtmcsmap |= (vhtmcs << 14);
case IEEE80211_VHT_NSS7:
vhtmcsmap &= 0xCFFF;
vhtmcsmap |= (vhtmcs << 12);
case IEEE80211_VHT_NSS6:
vhtmcsmap &= 0xF3FF;
vhtmcsmap |= (vhtmcs << 10);
case IEEE80211_VHT_NSS5:
vhtmcsmap &= 0xFCFF;
vhtmcsmap |= (vhtmcs << 8);
case IEEE80211_VHT_NSS4:
vhtmcsmap &= 0xFF3F;
vhtmcsmap |= (vhtmcs << 6);
case IEEE80211_VHT_NSS3:
vhtmcsmap &= 0xFFCF;
vhtmcsmap |= (vhtmcs << 4);
case IEEE80211_VHT_NSS2:
vhtmcsmap &= 0xFFF3;
vhtmcsmap |= (vhtmcs << 2);
case IEEE80211_VHT_NSS1:
default: /* At least 1 spatial stream supported */
vhtmcsmap &= 0xFFFC;
vhtmcsmap |= vhtmcs;
break;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_WLAN,
"vhtmcsmap: %x for NSS=%d & MCS=%d \n", vhtmcsmap, vhtnss, vhtmcs);
return (vhtmcsmap);
}
static void qdrv_wlan_80211_set_vht_mcsset(struct ieee80211_vhtcap *vhtcap, enum ieee80211_vht_nss vht_nss_cap,
enum ieee80211_vht_mcs_supported vht_mcs_cap)
{
enum ieee80211_vht_nss max_vht_tx_nss_cap;
enum ieee80211_vht_nss max_vht_rx_nss_cap;
if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0)) {
max_vht_tx_nss_cap = IEEE80211_VHT_NSS2;
max_vht_rx_nss_cap = IEEE80211_VHT_NSS2;
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) {
max_vht_tx_nss_cap = IEEE80211_VHT_NSS2;
max_vht_rx_nss_cap = IEEE80211_VHT_NSS4;
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) {
max_vht_tx_nss_cap = IEEE80211_VHT_NSS3;
max_vht_rx_nss_cap = IEEE80211_VHT_NSS3;
} else {
max_vht_tx_nss_cap = IEEE80211_VHT_NSS4;
max_vht_rx_nss_cap = IEEE80211_VHT_NSS4;
}
vhtcap->rxmcsmap = qdrv_wlan_80211_vhtmcs_map(min(max_vht_rx_nss_cap, vht_nss_cap),
vht_mcs_cap);
vhtcap->txmcsmap = qdrv_wlan_80211_vhtmcs_map(min(max_vht_tx_nss_cap, vht_nss_cap),
vht_mcs_cap);
}
static int qdrv_wlan_80211_get_11ac_mode(struct ieee80211com *ic)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_WLAN,
"802.11ac mode = %d\n", ic->ic_phymode);
if (IS_IEEE80211_VHT_ENABLED(ic)) {
return (QTN_11NAC_ENABLE);
} else {
return (QTN_11NAC_DISABLE);
}
}
static void qdrv_wlan_80211_set_11ac_mode(struct ieee80211com *ic, int vht)
{
#ifdef QDRV_FEATURE_HT
int ic_phymode_save = ic->ic_phymode;
/*
* phymode has already been initialized through set_bw
* - need to reinitialize if in 11ac mode
*/
if (vht == QTN_11NAC_ENABLE) {
if (!IS_IEEE80211_VHT_ENABLED(ic)) {
if (ic->ic_phymode == IEEE80211_MODE_11NA)
ic->ic_phymode = IEEE80211_MODE_11AC_VHT20PM;
else if (ic->ic_phymode == IEEE80211_MODE_11NA_HT40PM)
ic->ic_phymode = IEEE80211_MODE_11AC_VHT40PM;
else
ic->ic_phymode = IEEE80211_MODE_11AC_VHT80PM;
}
} else {
if ((ic->ic_phymode == IEEE80211_MODE_11AC_VHT80PM) ||
(ic->ic_phymode == IEEE80211_MODE_11AC_VHT40PM))
ic->ic_phymode = IEEE80211_MODE_11NA_HT40PM;
else if (ic->ic_phymode == IEEE80211_MODE_11AC_VHT20PM)
ic->ic_phymode = IEEE80211_MODE_11NA;
}
if ((vht == QTN_11NAC_ENABLE) && !ieee80211_swfeat_is_supported(SWFEAT_ID_VHT, 1))
ic->ic_phymode = ic_phymode_save;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_WLAN,
"802.11ac mode = %d\n", ic->ic_phymode);
#endif
}
#ifdef CONFIG_QVSP
static void qdrv_wlan_notify_qvsp_coc_state_changed(struct qvsp_s *qvsp, struct ieee80211com *ic)
{
if (qvsp) {
if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_DUTY) {
qvsp_inactive_flag_set(qvsp, QVSP_INACTIVE_COC);
} else {
qvsp_inactive_flag_clear(qvsp, QVSP_INACTIVE_COC);
}
}
}
#endif
static void qdrv_wlan_notify_pm_state_changed(struct ieee80211com *ic, int pm_level_prev)
{
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
const char *tag = QEVT_PM_PREFIX;
const char *msg = "PM-LEVEL-CHANGE";
if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] != pm_level_prev &&
ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] != BOARD_PM_LEVEL_FORCE_NO) {
qdrv_eventf(vap->iv_dev, "%s%s from %u to %u", tag, msg,
(unsigned)pm_level_prev,
(unsigned)ic->ic_pm_state[QTN_PM_CURRENT_LEVEL]);
}
}
static void qdrv_send_to_l2_ext_filter(struct ieee80211vap *vap, struct sk_buff *skb)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct qdrv_wlan *qw = qv->parent;
qdrv_tqe_send_l2_ext_filter(qw, skb);
}
static void qdrv_wlan_80211_setparam(struct ieee80211_node *ni, int param,
int value, unsigned char *data, int len)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct ieee80211vap *vap = &qv->iv;
struct qdrv_wlan *qw = qv->parent;
struct ieee80211com *ic = &qw->ic;
struct qtn_setparams_args *args = NULL;
dma_addr_t args_dma;
dma_addr_t ctrl_dma;
struct host_ioctl *ioctl;
u_int8_t tid;
u_int16_t seq, size, time;
struct device *dev = qdrv_soc_get_addr_dev();
switch (param) {
case IEEE80211_PARAM_FORCE_MUC_TRACE:
qdrv_muc_traceback(value == 0xdead ? 1 : 0);
return;
case IEEE80211_PARAM_FORCE_ENABLE_TRIGGERS:
g_triggers_on = value;
return;
case IEEE80211_PARAM_FORCE_MUC_HALT:
qdrv_halt_muc();
return;
case IEEE80211_PARAM_HTBA_SEQ_CTRL:
seq = value & 0xFFFF;
tid = (value & 0xFF0000) >> 16;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_HTBA_SEQ_CTRL (%d), %d, %d\n",
param, tid, seq);
ni->ni_ba_tx[tid].seq = seq;
return;
case IEEE80211_PARAM_HTBA_SIZE_CTRL:
size = value & 0xFFFF;
tid = (value & 0xFF0000) >> 16;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_HTBA_SIZE_CTRL (%d), %d, %d\n",
param, tid, size);
ni->ni_ba_tx[tid].buff_size = size;
return;
case IEEE80211_PARAM_HTBA_TIME_CTRL:
time = value & 0xFFFF;
tid = (value & 0xFF0000) >> 16;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_HTBA_TIME_CTRL (%d), %d, %d\n",
param, tid, time);
ni->ni_ba_tx[tid].timeout = time;
return;
case IEEE80211_PARAM_HT_ADDBA:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_HT_ADDBA (%d)\n", param);
qdrv_wlan_80211_send_addba(ni, value);
return;
case IEEE80211_PARAM_HT_DELBA:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_HT_DELBA (%d)\n", param);
qdrv_wlan_drop_ba(ni, value, 1, IEEE80211_REASON_UNSPECIFIED);
qdrv_wlan_drop_ba(ni, value, 0, IEEE80211_REASON_UNSPECIFIED);
return;
case IEEE80211_PARAM_TXBF_CTRL:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_TXBF_CTRL (%d)\n", param);
qdrv_txbf_config_set((struct qdrv_wlan *) qv->parent, value);
return;
case IEEE80211_PARAM_BW_SEL_MUC:
case IEEE80211_PARAM_BW_SEL:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_BW_SEL_MUC(%d)\n", param);
qdrv_wlan_80211_set_cap_bw(ni, value);
if (qv->iv.iv_opmode == IEEE80211_M_HOSTAP)
qdrv_wlan_80211_beacon_update((struct ieee80211vap *)qv);
break;
case IEEE80211_PARAM_SHORT_GI:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_SHORT_GI(%d)\n", param);
qdrv_wlan_80211_set_cap_sgi(ni, value);
break;
case IEEE80211_PARAM_LDPC:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_LDPC(%d)\n", param);
qdrv_wlan_80211_set_ldpc(ni, value);
break;
case IEEE80211_PARAM_STBC:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_STBC(%d)\n", param);
qdrv_wlan_80211_set_stbc(ni, value);
break;
case IEEE80211_PARAM_RTS_CTS:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_RTS_CTS(%d)\n", param);
qdrv_wlan_80211_set_rts_cts(ni, value);
break;
case IEEE80211_PARAM_TX_QOS_SCHED:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_TX_QOS_SCHED(%d)\n", param);
ni->ni_ic->ic_tx_qos_sched = (value & 0xf);
break;
case IEEE80211_PARAM_PEER_RTS_MODE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_PEER_RTS_MODE(%d)\n", param);
qdrv_wlan_80211_set_peer_rts_mode(ni, value);
break;
case IEEE80211_PARAM_DYN_WMM:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_DYN_WMM(%d)\n", param);
ic->ic_dyn_wmm = value;
break;
case IEEE80211_PARAM_GET_CH_INUSE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_GET_CH_INUSE(%d)\n", param);
if (value)
ic->ic_flags_qtn |= IEEE80211_QTN_PRINT_CH_INUSE;
else
ic->ic_flags_qtn &= ~IEEE80211_QTN_PRINT_CH_INUSE;
break;
case IEEE80211_PARAM_11N_40_ONLY_MODE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_11N_40_ONLY_MODE (%d)\n", param);
qdrv_wlan_80211_set_11n40_only_mode(ni, value);
break;
case IEEE80211_PARAM_MAX_MGMT_FRAMES:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_MAX_MGMT_FRAMES (%d)\n", param);
qw->tx_if.txdesc_cnt[QDRV_TXDESC_MGMT] = value;
break;
case IEEE80211_PARAM_MCS_ODD_EVEN:
qw->mcs_odd_even = value;
break;
case IEEE80211_PARAM_RESTRICTED_MODE:
qw->tx_restrict = value;
break;
case IEEE80211_PARAM_RESTRICT_RTS:
qw->tx_restrict_rts = value;
break;
case IEEE80211_PARAM_RESTRICT_LIMIT:
qw->tx_restrict_limit = value;
break;
case IEEE80211_PARAM_RESTRICT_RATE:
qw->tx_restrict_rate = value;
break;
case IEEE80211_PARAM_SWRETRY_AGG_MAX:
qw->tx_swretry_agg_max = value;
break;
case IEEE80211_PARAM_SWRETRY_NOAGG_MAX:
qw->tx_swretry_noagg_max = value;
break;
case IEEE80211_PARAM_SWRETRY_SUSPEND_XMIT:
qw->tx_swretry_suspend_xmit = value;
break;
case IEEE80211_PARAM_TEST_LNCB:
if (value) {
qw->flags_ext |= QDRV_WLAN_DEBUG_TEST_LNCB;
} else {
qw->flags_ext &= ~QDRV_WLAN_DEBUG_TEST_LNCB;
}
break;
case IEEE80211_PARAM_UNKNOWN_DEST_ARP:
if (value) {
qw->flags_ext |= QDRV_WLAN_FLAG_UNKNOWN_ARP;
} else {
qw->flags_ext &= ~QDRV_WLAN_FLAG_UNKNOWN_ARP;
}
break;
case IEEE80211_PARAM_MUC_FLAGS:
case IEEE80211_PARAM_HT_NSS_CAP:
qdrv_wlan_80211_set_mcsset(ic);
qdrv_wlan_80211_set_mcsparams(ic);
break;
case IEEE80211_PARAM_VHT_MCS_CAP:
case IEEE80211_PARAM_VHT_NSS_CAP:
qdrv_wlan_80211_set_vht_mcsset(&ic->ic_vhtcap, ic->ic_vht_nss_cap, ic->ic_vht_mcs_cap);
qdrv_wlan_80211_set_vht_mcsset(&ic->ic_vhtcap_24g, ic->ic_vht_nss_cap_24g, ic->ic_vht_mcs_cap);
break;
case IEEE80211_PARAM_UNKNOWN_DEST_FWD:
if (value) {
qw->flags_ext |= QDRV_WLAN_FLAG_UNKNOWN_FWD;
} else {
qw->flags_ext &= ~QDRV_WLAN_FLAG_UNKNOWN_FWD;
}
break;
case IEEE80211_PARAM_PWR_SAVE: {
uint32_t pm_param = QTN_PM_UNPACK_PARAM(value);
uint32_t pm_value = QTN_PM_UNPACK_VALUE(value);
int level_prev = ic->ic_pm_state[QTN_PM_CURRENT_LEVEL];
if (pm_param < QTN_PM_IOCTL_MAX) {
ic->ic_pm_state[pm_param] = pm_value;
#ifdef CONFIG_QVSP
qdrv_wlan_notify_qvsp_coc_state_changed(qw->qvsp, ic);
#endif
qdrv_wlan_notify_pm_state_changed(ic, level_prev);
}
if (pm_param == QTN_PM_PDUTY_PERIOD_MS &&
pm_qos_requirement(PM_QOS_POWER_SAVE) >= BOARD_PM_LEVEL_DUTY) {
if (ic->ic_lintval != ieee80211_pm_period_tu(ic)) {
/* Configure beacon interval to power duty interval */
ieee80211_beacon_interval_set(ic, ieee80211_pm_period_tu(ic));
}
}
break;
}
case IEEE80211_PARAM_TEST_TRAFFIC:
value = msecs_to_jiffies(value);
if (value != vap->iv_test_traffic_period) {
vap->iv_test_traffic_period = value;
if (value == 0) {
del_timer(&vap->iv_test_traffic);
} else {
mod_timer(&vap->iv_test_traffic,
jiffies + vap->iv_test_traffic_period);
}
}
break;
case IEEE80211_PARAM_QCAT_STATE: {
struct net_device *dev = qv->iv.iv_dev;
qdrv_eventf(dev, "QCAT state=%d", value);
printk("QCAT state=%d\n", value);
TXSTAT_SET(qw, qcat_state, value);
break;
}
case IEEE80211_PARAM_MIMOMODE:
qw->tx_mimomode = value;
break;
case IEEE80211_PARAM_SHORT_RETRY_LIMIT:
case IEEE80211_PARAM_LONG_RETRY_LIMIT:
//Currenty don't supporte to set this param, because we don't implement this feature in MacFW.
break;
case IEEE80211_PARAM_RETRY_COUNT:
qdrv_wlan_80211_set_retry_count(ni, value);
break;
case IEEE80211_PARAM_LEGACY_RETRY_LIMIT:
qdrv_wlan_80211_set_legacy_retry(ni, value);
break;
case IEEE80211_PARAM_RTSTHRESHOLD:
/* pass through, let the rts threshold value packed as normal param below */
break;
case IEEE80211_PARAM_CARRIER_ID:
g_carrier_id = value;
break;
case IEEE80211_PARAM_TX_QUEUING_ALG:
qw->tx_sch_shared_data->queuing_alg = value;
break;
case IEEE80211_PARAM_BA_THROT:
#ifdef CONFIG_QVSP
qdrv_wlan_manual_ba_throt(qw, qv, value);
#endif
return;
case IEEE80211_PARAM_WME_THROT:
#ifdef CONFIG_QVSP
qdrv_wlan_manual_wme_throt(qw, qv, value);
#endif
return;
case IEEE80211_PARAM_MODE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_11AC_MODE (%d)\n", param);
qdrv_wlan_80211_set_11ac_mode(ic, value);
if (qv->iv.iv_opmode == IEEE80211_M_HOSTAP) {
qdrv_wlan_80211_beacon_update((struct ieee80211vap *)qv);
}
break;
case IEEE80211_PARAM_GENPCAP:
if (qdrv_genpcap_set(qw, value, &ctrl_dma) == 0) {
data = (uint8_t *) &ctrl_dma;
len = sizeof(ctrl_dma);
}
break;
case IEEE80211_PARAM_TXBF_PERIOD:
if (!value) {
/*
* Turn off BF capabilities in the beacon when bfoff. Should
* work for both AP beamformer and STA beamformee disabling
* when bfoff is set.
* */
ic->ic_vhtcap.cap_flags &=
~(IEEE80211_VHTCAP_C_SU_BEAM_FORMER_CAP |
IEEE80211_VHTCAP_C_SU_BEAM_FORMEE_CAP);
ic->ic_vhtcap_24g.cap_flags &=
~(IEEE80211_VHTCAP_C_SU_BEAM_FORMER_CAP |
IEEE80211_VHTCAP_C_SU_BEAM_FORMEE_CAP);
} else {
ic->ic_vhtcap.cap_flags |=
(IEEE80211_VHTCAP_C_SU_BEAM_FORMER_CAP |
IEEE80211_VHTCAP_C_SU_BEAM_FORMEE_CAP);
ic->ic_vhtcap_24g.cap_flags |=
(IEEE80211_VHTCAP_C_SU_BEAM_FORMER_CAP |
IEEE80211_VHTCAP_C_SU_BEAM_FORMEE_CAP);
}
ic->ic_txbf_period = value;
if (qv->iv.iv_opmode == IEEE80211_M_HOSTAP)
qdrv_wlan_80211_beacon_update((struct ieee80211vap *)qv);
break;
case IEEE80211_PARAM_CONFIG_PMF:
if (qv->iv.iv_opmode == IEEE80211_M_HOSTAP)
qdrv_wlan_80211_beacon_update((struct ieee80211vap *)qv);
break;
case IEEE80211_PARAM_WOWLAN:
if ((IEEE80211_WOWLAN_HOST_POWER_SAVE == (value>>16)) &&
(1 == (value & 0xffff))) {
#ifndef TOPAZ_AMBER_IP
gpio_config(WOWLAN_GPIO_OUTPUT_PIN, GPIO_MODE_OUTPUT);
gpio_wowlan_output(WOWLAN_GPIO_OUTPUT_PIN, 0);
#else
/*
* In Amber WOWLAN is handled by WIFI2SOC interrupt.
*/
#endif
}
break;
case IEEE80211_PARAM_MAX_AGG_SIZE:
ic->ic_tx_max_ampdu_size = value;
break;
case IEEE80211_PARAM_RX_AGG_TIMEOUT:
ic->ic_rx_agg_timeout = value;
break;
case IEEE80211_PARAM_RESTRICT_WLAN_IP:
qw->restrict_wlan_ip = !!value;
break;
case IEEE80211_PARAM_OFF_CHAN_SUSPEND:
qdrv_hostlink_suspend_off_chan(qw, !!value);
break;
case IEEE80211_PARAM_CCA_FIXED:
ic->cca_fix_disable = !!value;
break;
case IEEE80211_PARAM_AUTO_CCA_ENABLE:
ic->auto_cca_enable = !!value;
break;
case IEEE80211_PARAM_BEACON_HANG_TIMEOUT:
ic->ic_bcn_hang_timeout = value;
break;
case IEEE80211_PARAM_VMODE:
qdrv_calcmd_set_tx_power(dev, value);
break;
case IEEE80211_PARAM_BB_DEAFNESS_WAR_EN:
ic->bb_deafness_war_disable = !!value;
break;
default:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"<0x%08x> (%d)\n", param, param);
break;
}
/* Make sure the data fits if it is provided */
if (data != NULL && len > sizeof(args->ni_data)) {
DBGPRINTF_E("Unable to transport %d bytes of data (max is %d)\n",
len, sizeof(args->ni_data));
return;
}
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate SETPARAM message\n");
vnet_free_ioctl(ioctl);
return;
}
/* Copy the values over */
args->ni_param = param;
if (param == IEEE80211_PARAM_MODE) {
args->ni_value = ic->ic_phymode;
} else {
args->ni_value = value;
}
args->ni_len = 0;
if (data != NULL && len > 0) {
memcpy(args->ni_data, data, len);
args->ni_len = len;
if (param == IEEE80211_PARAM_UPDATE_MU_GRP) {
/* place the sta's mac addr at the end of its group/pos arrays */
memcpy(&args->ni_data[len], &ni->ni_macaddr[0], IEEE80211_ADDR_LEN);
args->ni_len += IEEE80211_ADDR_LEN;
}
}
ioctl->ioctl_command = IOCTL_DEV_SETPARAMS;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static int qdrv_wlan_set_l2_ext_filter_port(struct ieee80211vap *vap, int port)
{
struct net_device *pcie_dev;
int cfg;
int cfg_val;
int cfg_mask;
uint8_t tqe_port;
switch (port) {
case L2_EXT_FILTER_EMAC_0_PORT:
cfg = BOARD_CFG_EMAC0;
cfg_mask = EMAC_IN_USE;
tqe_port = TOPAZ_TQE_EMAC_0_PORT;
break;
case L2_EXT_FILTER_EMAC_1_PORT:
cfg = BOARD_CFG_EMAC1;
cfg_mask = EMAC_IN_USE;
tqe_port = TOPAZ_TQE_EMAC_1_PORT;
break;
case L2_EXT_FILTER_PCIE_PORT:
cfg = BOARD_CFG_PCIE;
cfg_mask = PCIE_IN_USE;
/*
* PCIE TQE port is determined at runtime
*/
pcie_dev = dev_get_by_name(&init_net, "pcie0");
if (!pcie_dev) {
printk("QDRV: Error setting L2 external filter port: no pcie0 device\n");
return -ENODEV;
}
tqe_port = pcie_dev->if_port;
dev_put(pcie_dev);
break;
default:
printk("QDRV: Error setting L2 external filter port: port %d is invalid\n", port);
return -EINVAL;
}
if (get_board_config(cfg, &cfg_val) < 0) {
printk("QDRV: Error setting L2 external filter port: error getting board config\n");
return -ENODEV;
}
if (!(cfg_val & cfg_mask)) {
printk("QDRV: Error setting L2 external filter port: no such port\n");
return -ENODEV;
}
g_l2_ext_filter_port = tqe_port;
qdrv_wlan_80211_setparam(vap->iv_bss, IEEE80211_PARAM_L2_EXT_FILTER_PORT,
g_l2_ext_filter_port, NULL, 0);
return 0;
}
static int qdrv_wlan_set_l2_ext_filter(struct ieee80211vap *vap, int enable)
{
int ret;
if (enable && (g_l2_ext_filter_port == TOPAZ_TQE_NUM_PORTS)) {
ret = qdrv_wlan_set_l2_ext_filter_port(vap, L2_EXT_FILTER_DEF_PORT);
if (ret < 0)
return ret;
}
g_l2_ext_filter = !!enable;
qdrv_wlan_80211_setparam(vap->iv_bss, IEEE80211_PARAM_L2_EXT_FILTER,
g_l2_ext_filter, NULL, 0);
return 0;
}
static int qdrv_wlan_get_l2_ext_filter_port(void)
{
if (g_l2_ext_filter_port == TOPAZ_TQE_NUM_PORTS) {
return L2_EXT_FILTER_DEF_PORT;
} else if (g_l2_ext_filter_port == TOPAZ_TQE_EMAC_0_PORT) {
return L2_EXT_FILTER_EMAC_0_PORT;
} else if (g_l2_ext_filter_port == TOPAZ_TQE_EMAC_1_PORT) {
return L2_EXT_FILTER_EMAC_1_PORT;
} else {
return L2_EXT_FILTER_PCIE_PORT;
}
}
static __sram_text void
qdrv_wlan_stats_prot_ip(struct qdrv_wlan *qw, uint8_t is_tx, uint8_t ip_proto)
{
switch (ip_proto) {
case IPPROTO_UDP:
QDRV_STAT(qw, is_tx, prot_ip_udp);
break;
case IPPROTO_TCP:
QDRV_STAT(qw, is_tx, prot_ip_tcp);
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
QDRV_STAT(qw, is_tx, prot_ip_icmp);
break;
case IPPROTO_IGMP:
QDRV_STAT(qw, is_tx, prot_ip_igmp);
break;
default:
DBGPRINTF(DBG_LL_NOTICE, is_tx ? QDRV_LF_PKT_TX : QDRV_LF_PKT_RX,
"%s ip pkt type %u\n",
is_tx ? "tx" : "rx", ip_proto);
QDRV_STAT(qw, is_tx, prot_ip_other);
break;
}
}
__sram_text void
qdrv_wlan_stats_prot(struct qdrv_wlan *qw, uint8_t is_tx, uint16_t ether_type, uint8_t ip_proto)
{
switch (ether_type) {
case 0:
break;
case __constant_htons(ETH_P_IP):
qdrv_wlan_stats_prot_ip(qw, is_tx, ip_proto);
break;
case __constant_htons(ETH_P_IPV6):
QDRV_STAT(qw, is_tx, prot_ipv6);
qdrv_wlan_stats_prot_ip(qw, is_tx, ip_proto);
break;
case __constant_htons(ETH_P_ARP):
QDRV_STAT(qw, is_tx, prot_arp);
break;
case __constant_htons(ETH_P_PAE):
QDRV_STAT(qw, is_tx, prot_pae);
break;
default:
DBGPRINTF(DBG_LL_NOTICE, is_tx ? QDRV_LF_PKT_TX : QDRV_LF_PKT_RX,
"%s pkt type 0x%04x\n",
is_tx ? "tx" : "rx", ether_type);
QDRV_STAT(qw, is_tx, prot_other);
break;
}
}
/*
* This function performs proxy ARP for 3-address stations. It is intended for use
* with HS 2.0 vaps, which do not support 4-address stations.
*/
int qdrv_proxy_arp(struct ieee80211vap *iv,
struct qdrv_wlan *qw,
struct ieee80211_node *ni_rx,
uint8_t *data_start)
{
struct ieee80211_node *ni_target;
struct ether_arp *arp = (struct ether_arp *)data_start;
uint32_t s_ipaddr = get_unaligned((u32 *)&arp->arp_spa);
uint32_t t_ipaddr = get_unaligned((u32 *)&arp->arp_tpa);
int gratuitous_arp = (s_ipaddr == t_ipaddr);
uint8_t macaddr[IEEE80211_ADDR_LEN];
if (gratuitous_arp) {
/*
* If the ARP announcement came from an associated station,
* update the node's IP address.
*/
if (ni_rx && (arp->ea_hdr.ar_op == __constant_htons(ARPOP_REQUEST))
&& IEEE80211_ADDR_EQ(ni_rx->ni_macaddr, arp->arp_sha)) {
if (ni_rx->ni_ip_addr == t_ipaddr)
return 1;
ni_target = ieee80211_find_node_by_ip_addr(iv, t_ipaddr);
if (ni_target) {
ni_target->ni_ip_addr = 0;
ieee80211_free_node(ni_target);
}
ni_rx->ni_ip_addr = t_ipaddr;
}
return 1;
}
if (arp->ea_hdr.ar_op == __constant_htons(ARPOP_REQUEST)) {
if (ipv4_is_loopback(t_ipaddr) || ipv4_is_multicast(t_ipaddr) ||
ipv4_is_zeronet(t_ipaddr)) {
return 0;
}
ni_target = ieee80211_find_node_by_ip_addr(iv, t_ipaddr);
if (ni_target) {
IEEE80211_ADDR_COPY(macaddr, ni_target->ni_macaddr);
ieee80211_free_node(ni_target);
arp_send(ARPOP_REPLY, ETH_P_ARP, s_ipaddr, qw->br_dev, t_ipaddr,
arp->arp_sha, macaddr, arp->arp_sha);
return 1;
}
}
return 0;
}
#ifdef CONFIG_IPV6
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
static struct sk_buff * qdrv_build_neigh_adv_skb(struct net_device *dev,
const struct in6_addr *daddr, const struct in6_addr *saddr,
uint8_t *src_mac, uint8_t *dest_mac, struct icmp6hdr *icmp6h,
const struct in6_addr *target, int llinfo)
{
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
struct ether_header *eh;
uint8_t *opt;
int len;
int err;
len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
if (llinfo) {
len += NDISC_OPT_SPACE(IEEE80211_ADDR_LEN + ndisc_addr_option_pad(dev->type));
/* type(1byte) + len(1byte) + Dev addr len + pad */
}
skb = sock_alloc_send_skb(sk, (MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_ALLOCATED_SPACE(dev)), 1, &err);
if (!skb) {
DBGPRINTF_LIMIT_E("%s: failed to allocate an skb, err=%d\n",
__func__, err);
return NULL;
}
skb->dev = dev;
skb->priority = WME_AC_VO;
eh = (struct ether_header *)skb->tail;
IEEE80211_ADDR_COPY(eh->ether_dhost, dest_mac);
IEEE80211_ADDR_COPY(eh->ether_shost, src_mac);
eh->ether_type = htons(ETH_P_IPV6);
skb_put(skb, sizeof(*eh));
skb->data += sizeof(*eh);
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
skb->data -= sizeof(*eh);
skb->transport_header = skb->tail;
skb_put(skb, len);
hdr = (struct icmp6hdr *)skb_transport_header(skb);
memcpy(hdr, icmp6h, sizeof(*hdr));
opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
if (target) {
ipv6_addr_copy((struct in6_addr *)opt, target);
opt += sizeof(*target);
}
if (llinfo) {
ndisc_fill_addr_option(opt, llinfo, src_mac,
IEEE80211_ADDR_LEN, dev->type);
}
hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
IPPROTO_ICMPV6,
csum_partial(hdr,len, 0));
return skb;
}
#else
static struct sk_buff *qdrv_build_neigh_adv_skb(struct net_device *dev,
const struct in6_addr *daddr, const struct in6_addr *target,
uint8_t *src_mac, uint8_t *dest_mac, struct icmp6hdr *icmp6h)
{
struct sk_buff *skb;
struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
struct ether_header *eh;
struct nd_msg *msg;
int len = sizeof(struct nd_msg) + NDISC_OPT_SPACE(dev->addr_len);
skb = ndisc_alloc_skb(dev, len);
if (!skb)
return NULL;
skb->priority = QTN_TID_VO;
msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
memcpy(&msg->icmph, icmp6h, sizeof(msg->icmph));
msg->target = *target;
ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, src_mac);
msg->icmph.icmp6_cksum = csum_ipv6_magic(target, daddr, len,
IPPROTO_ICMPV6,
csum_partial(msg, len, 0));
ip6_nd_hdr(skb, target, daddr, inet6_sk(sk)->hop_limit, len);
eh = (struct ether_header *)skb_push(skb, sizeof(*eh));
IEEE80211_ADDR_COPY(eh->ether_dhost, dest_mac);
IEEE80211_ADDR_COPY(eh->ether_shost, src_mac);
eh->ether_type = htons(ETH_P_IPV6);
return skb;
}
#endif
static int qdrv_send_neigh_adv(struct net_device *dev, const struct in6_addr *daddr,
const struct in6_addr *solicited_addr, uint8_t *src_mac,
uint8_t *dest_mac, int router, int solicited, int override, int llinfo)
{
struct sk_buff *skb;
struct icmp6hdr icmp6h = {
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
};
icmp6h.icmp6_router = router;
icmp6h.icmp6_solicited = solicited;
icmp6h.icmp6_override = override;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
skb = qdrv_build_neigh_adv_skb(dev, daddr, solicited_addr, src_mac, dest_mac, &icmp6h);
#else
skb = qdrv_build_neigh_adv_skb(dev, daddr, solicited_addr, src_mac, dest_mac,
&icmp6h, solicited_addr, llinfo ? ND_OPT_TARGET_LL_ADDR : 0);
#endif
if (!skb)
return 1;
dev_queue_xmit(skb);
return 0;
}
#endif
#ifdef CONFIG_IPV6
static int qdrv_wlan_handle_neigh_sol(struct ieee80211vap *vap, struct qdrv_wlan *qw, void *proto_data,
uint8_t *data_start, struct ether_header *eh, uint8_t in_tx)
{
struct ipv6hdr *ipv6 = (struct ipv6hdr *)data_start;
struct nd_msg *msg = (struct nd_msg *)proto_data;
struct in6_addr *saddr = &ipv6->saddr;
struct in6_addr *daddr = &ipv6->daddr;
struct in6_addr target;
int dup_addr_detect = ipv6_addr_any(saddr);
const struct in6_addr qdrv_in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
uint8_t all_node_mc_mac_addr[] = {0x33, 0x33, 0x00, 0x00, 0x00, 0x01};
struct ieee80211_node_table *nt = &qw->ic.ic_sta;
struct ieee80211_node *ni;
uint8_t *dest_mac;
uint8_t src_mac[IEEE80211_ADDR_LEN];
int llinfo;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
iputil_in6_addr_copy(saddr, &ipv6->saddr);
iputil_in6_addr_copy(daddr, &ipv6->daddr);
iputil_in6_addr_copy(&target, &msg->target);
dup_addr_detect = ipv6_addr_any(saddr);
if (!iputil_ipv6_is_neigh_sol_msg(dup_addr_detect, &target, daddr))
return 1;
#else
if (!iputil_ipv6_is_neigh_sol_msg(dup_addr_detect, msg, ipv6))
return 1;
#endif
if (!qw->br_dev)
return 1;
if (dup_addr_detect) {
/* Duplicate address detection */
ni = ieee80211_find_node_by_ipv6_addr(vap, &msg->target);
if (ni && !IEEE80211_ADDR_EQ(ni->ni_macaddr, eh->ether_shost)) {
if (in_tx) {
/* send multicast neighbour advertisement frame to back end only */
dest_mac = all_node_mc_mac_addr;
} else {
/* send unicast neighbour advertisement frame to STA */
dest_mac = eh->ether_shost;
}
IEEE80211_ADDR_COPY(src_mac, ni->ni_macaddr);
ieee80211_free_node(ni);
qdrv_send_neigh_adv(qw->br_dev, &qdrv_in6addr_linklocal_allnodes,
&msg->target, src_mac, dest_mac,
false, false, true, true);
return 1;
} else if (ni) {
ieee80211_free_node(ni);
return 1;
}
ni = ieee80211_find_node(nt, eh->ether_shost);
if (ni && (IEEE80211_AID(ni->ni_associd) != 0)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
ni->ipv6_llocal = target;
#else
ipv6_addr_copy(&ni->ipv6_llocal, &msg->target);
#endif
ieee80211_free_node(ni);
} else if (ni) {
ieee80211_free_node(ni);
}
return 1;
}
ni = ieee80211_find_node_by_ipv6_addr(vap, &msg->target);
if (ni && IEEE80211_AID(ni->ni_associd) != 0) {
IEEE80211_ADDR_COPY(src_mac, ni->ni_macaddr);
ieee80211_free_node(ni);
llinfo = ipv6_addr_is_multicast(daddr);
qdrv_send_neigh_adv(qw->br_dev, saddr, &msg->target, src_mac,
eh->ether_shost, false, true, llinfo, llinfo);
return 1;
} else if (ni) {
ieee80211_free_node(ni);
return 1;
}
return 0;
}
#endif
#ifdef CONFIG_IPV6
int qdrv_wlan_handle_neigh_msg(struct ieee80211vap *vap, struct qdrv_wlan *qw,
uint8_t *data_start, uint8_t in_tx, struct sk_buff *skb,
uint8_t ip_proto, void *proto_data)
{
struct ipv6hdr *ipv6;
struct icmp6hdr *icmpv6;
struct iphdr *p_iphdr = (struct iphdr *)data_start;
struct ether_header *eh = (struct ether_header *) skb->data;
if (ip_proto == IPPROTO_ICMPV6) {
ipv6 = (struct ipv6hdr *)data_start;
icmpv6 = (struct icmp6hdr *)proto_data;
switch(icmpv6->icmp6_type) {
case NDISC_NEIGHBOUR_ADVERTISEMENT:
case NDISC_NEIGHBOUR_SOLICITATION:
if (!iputil_ipv6_is_neigh_msg(ipv6, icmpv6))
return 1;
if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
/* Verify unsolicited neighbour advertisement */
if (iputil_ipv6_is_ll_all_nodes_mc(eh->ether_dhost, p_iphdr) &&
!icmpv6->icmp6_solicited && in_tx) {
return 1;
}
}
if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
if (qdrv_wlan_handle_neigh_sol(vap, qw, proto_data,
data_start, eh, in_tx) || in_tx)
return 1;
}
break;
default:
return 0;
}
}
return 0;
}
#endif
static int qdrv_wlan_80211_get_phy_stats(struct net_device *dev,
struct ieee80211com *ic,
struct ieee80211_phy_stats *ps,
uint8_t all_stats)
{
struct qdrv_wlan *qw;
struct qdrv_mac *mac;
qw = container_of(ic, struct qdrv_wlan, ic);
mac = qw->mac;
return qdrv_muc_get_last_phy_stats(mac, ic, ps, all_stats);
}
static int qdrv_wlan_80211_get_ldpc(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->ldpc_enabled);
}
static int qdrv_wlan_80211_get_stbc(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->stbc_enabled);
}
static int qdrv_wlan_80211_get_rts_cts(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->rts_cts_prot);
}
static int qdrv_wlan_80211_get_peer_rts_mode(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->ic_peer_rts_mode);
}
static int qdrv_wlan_80211_get_11n40_only_mode(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->ic_11n_40_only_mode);
}
static int qdrv_wlan_80211_get_legacy_retry_limit(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->ic_legacy_retry_limit);
}
static int qdrv_wlan_80211_get_retry_count(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return (ic->ic_retry_count);
}
static int qdrv_wlan_80211_get_rx_agg_timeout(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
return ic->ic_rx_agg_timeout;
}
static int qdrv_wlan_80211_get_cca_stats(struct net_device *dev,
struct ieee80211com *ic,
struct qtn_exp_cca_stats *cs)
{
struct qdrv_wlan *qw;
struct qdrv_mac *mac;
qw = container_of(ic, struct qdrv_wlan, ic);
mac = qw->mac;
return qdrv_muc_get_last_cca_stats(mac, ic, cs);
}
static int qdrv_is_gain_low(void)
{
struct device *dev = qdrv_soc_get_addr_dev();
uint32_t mixval;
uint32_t pgaval;
uint8_t lowgain_mixer;
uint8_t lowgain_pga;
#define RFMIX_LOAD_S 14
#define RFMIX_LOAD_M 0x1c000
#define RFMIX_PGA_S 2
#define RFMIX_PGA_M 0xc
mixval = qdrv_command_read_rf_reg(dev, 166);
pgaval = qdrv_command_read_rf_reg(dev, 168);
lowgain_mixer = (((mixval & RFMIX_LOAD_M) >> RFMIX_LOAD_S) == 0x7);
lowgain_pga = (((pgaval & RFMIX_PGA_M) >> RFMIX_PGA_S) == 0);
if (lowgain_mixer && lowgain_pga)
return 1;
else
return 0;
}
static int
qdrv_wlan_get_congestion_index(struct qdrv_wlan *qw)
{
#define QDRV_CONGEST_IX_ROUNDED(_pc) (((_pc) + 5) / 10)
struct qtn_stats_log *iw_stats_log = qw->mac->mac_sys_stats;
struct muc_tx_stats *tx_stats = NULL;
int congest_idx;
if (qw->pktlogger.stats_uc_tx_ptr == NULL && iw_stats_log != NULL) {
qw->pktlogger.stats_uc_tx_ptr =
ioremap_nocache(muc_to_lhost((u32)iw_stats_log->tx_muc_stats),
sizeof(struct muc_tx_stats));
}
tx_stats = (struct muc_tx_stats *)qw->pktlogger.stats_uc_tx_ptr;
if (!tx_stats)
return -EFAULT;
congest_idx = QDRV_CONGEST_IX_ROUNDED(tx_stats->cca_fat);
if ((congest_idx < 0) || (congest_idx > 10))
return -EFAULT;
return congest_idx;
}
static uint32_t qdrv_wlan_get_michael_errcnt(struct qdrv_wlan *qw)
{
struct qtn_stats_log *iw_stats_log = qw->mac->mac_sys_stats;
struct muc_rx_stats *rx_stats = NULL;
if (qw->pktlogger.stats_uc_rx_ptr == NULL && iw_stats_log != NULL) {
qw->pktlogger.stats_uc_rx_ptr =
ioremap_nocache(muc_to_lhost((u32)iw_stats_log->rx_muc_stats),
sizeof(struct muc_rx_stats));
}
rx_stats = (struct muc_rx_stats *)qw->pktlogger.stats_uc_rx_ptr;
if (!rx_stats)
return 0;
return rx_stats->rx_tkip_mic_err;
}
static void qdrv_get_mu_grp(struct ieee80211_node *ni,
struct qtn_mu_grp_args *mu_grp_tbl_cpy)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
dma_addr_t dma;
struct qtn_mu_grp_args *mu_grp_tbl;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(mu_grp_tbl = qdrv_hostlink_alloc_coherent(NULL,
sizeof(*mu_grp_tbl)*IEEE80211_MU_GRP_NUM_MAX,
&dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate DISASSOC message\n");
vnet_free_ioctl(ioctl);
return;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)mu_grp_tbl);
ioctl->ioctl_command = IOCTL_DEV_GET_MU_GRP;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = dma;
if (vnet_send_ioctl(qv, ioctl)) {
memcpy(mu_grp_tbl_cpy, mu_grp_tbl, sizeof(*mu_grp_tbl)*IEEE80211_MU_GRP_NUM_MAX);
}
qdrv_hostlink_free_coherent(NULL, sizeof(*mu_grp_tbl)*IEEE80211_MU_GRP_NUM_MAX,
mu_grp_tbl, dma);
}
static int32_t qdrv_get_mu_enable(struct ieee80211_node *ni)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
dma_addr_t dma;
int32_t *mu_enable_ptr;
int32_t mu_enable = -1;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(mu_enable_ptr = qdrv_hostlink_alloc_coherent(NULL,
sizeof(*mu_enable_ptr),
&dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate DISASSOC message\n");
vnet_free_ioctl(ioctl);
goto exit;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)mu_enable_ptr);
ioctl->ioctl_command = IOCTL_DEV_GET_MU_ENABLE;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = dma;
if (vnet_send_ioctl(qv, ioctl)) {
mu_enable = *mu_enable_ptr;
}
qdrv_hostlink_free_coherent(NULL, sizeof(*mu_enable_ptr), mu_enable_ptr, dma);
exit:
return mu_enable;
}
static int32_t qdrv_get_mu_grp_qmat(struct ieee80211_node *ni, uint8_t grp)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
dma_addr_t dma;
int32_t *prec_enable_ptr;
int32_t prec_enable = -1;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(prec_enable_ptr = qdrv_hostlink_alloc_coherent(NULL,
sizeof(*prec_enable_ptr),
&dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate DISASSOC message\n");
vnet_free_ioctl(ioctl);
goto exit;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)prec_enable_ptr);
ioctl->ioctl_command = IOCTL_DEV_GET_PRECODE_ENABLE;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = grp;
ioctl->ioctl_argp = dma;
if (vnet_send_ioctl(qv, ioctl)) {
prec_enable = *prec_enable_ptr;
}
qdrv_hostlink_free_coherent(NULL, sizeof(*prec_enable_ptr), prec_enable_ptr, dma);
exit:
return prec_enable;
}
static int32_t qdrv_get_mu_use_eq(struct ieee80211_node *ni)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
dma_addr_t dma;
int32_t *eq_enable_ptr;
int32_t eq_enable = -1;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(eq_enable_ptr = qdrv_hostlink_alloc_coherent(NULL,
sizeof(*eq_enable_ptr),
&dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate DISASSOC message\n");
vnet_free_ioctl(ioctl);
goto exit;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN, "(1)ioctl %p dma ptr %p\n", ioctl, (void *)eq_enable_ptr);
ioctl->ioctl_command = IOCTL_DEV_GET_MU_USE_EQ;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = dma;
if (vnet_send_ioctl(qv, ioctl)) {
eq_enable = *eq_enable_ptr;
}
qdrv_hostlink_free_coherent(NULL, sizeof(*eq_enable_ptr), eq_enable_ptr, dma);
exit:
return eq_enable;
}
static int qdrv_wlan_80211_getparam(struct ieee80211_node *ni, int param,
int *value, unsigned char *data, int *len)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct qdrv_wlan *qw = qv->parent;
struct ieee80211com *ic = &qw->ic;
struct qtn_stats_log *iw_stats_log = qw->mac->mac_sys_stats;
if (!ic->ic_muc_tx_stats) {
ic->ic_muc_tx_stats = (struct muc_tx_stats *) ioremap_nocache(
muc_to_lhost((u32)iw_stats_log->tx_muc_stats),
sizeof(struct muc_tx_stats));
}
static uint32_t keep_alive_cnt;
uint32_t val;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
switch (param) {
case IEEE80211_PARAM_TXBF_CTRL:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_TXBF_CTRL (%d)\n", param);
qdrv_txbf_config_get(qw, &val);
*value = val;
break;
case IEEE80211_PARAM_TXBF_PERIOD:
*value = ic->ic_txbf_period;
break;
case IEEE80211_PARAM_GET_RFCHIP_ID:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_GET_RFCHIP_ID (%d)\n", param);
*value = qw->rf_chipid;
break;
case IEEE80211_PARAM_GET_RFCHIP_VERID:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_GET_RFCHIP_VERID (%d)\n", param);
*value = qw->rf_chip_verid;
break;
case IEEE80211_PARAM_BW_SEL_MUC:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_BW_SEL_MUC (%d)\n", param);
*value = qdrv_wlan_80211_get_cap_bw(ni->ni_ic);
break;
case IEEE80211_PARAM_LDPC:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_LDPC (%d)\n", param);
*value = qdrv_wlan_80211_get_ldpc(ni);
break;
case IEEE80211_PARAM_STBC:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_STBC (%d)\n", param);
*value = qdrv_wlan_80211_get_stbc(ni);
break;
case IEEE80211_PARAM_RTS_CTS:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_RTS_CTS (%d)\n", param);
*value = qdrv_wlan_80211_get_rts_cts(ni);
break;
case IEEE80211_PARAM_TX_QOS_SCHED:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_TX_QOS_SCHED (%d)\n", param);
*value = ni->ni_ic->ic_tx_qos_sched;
break;
case IEEE80211_PARAM_PEER_RTS_MODE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_PEER_RTS_MODE (%d)\n", param);
*value = qdrv_wlan_80211_get_peer_rts_mode(ni);
break;
case IEEE80211_PARAM_DYN_WMM:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_DYN_WMM (%d)\n", param);
*value = ic->ic_dyn_wmm;
break;
case IEEE80211_PARAM_11N_40_ONLY_MODE:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_11N_40_ONLY_MODE (%d)\n", param);
*value = qdrv_wlan_80211_get_11n40_only_mode(ni);
break;
case IEEE80211_PARAM_MAX_MGMT_FRAMES:
*value = qw->tx_if.txdesc_cnt[QDRV_TXDESC_MGMT];
break;
case IEEE80211_PARAM_MCS_ODD_EVEN:
*value = qw->mcs_odd_even;
break;
case IEEE80211_PARAM_RESTRICTED_MODE:
*value = qw->tx_restrict;
break;
case IEEE80211_PARAM_RESTRICT_RTS:
*value = qw->tx_restrict_rts;
break;
case IEEE80211_PARAM_RESTRICT_LIMIT:
*value = qw->tx_restrict_limit;
break;
case IEEE80211_PARAM_RESTRICT_RATE:
*value = qw->tx_restrict_rate;
break;
case IEEE80211_PARAM_SWRETRY_AGG_MAX:
*value = qw->tx_swretry_agg_max;
break;
case IEEE80211_PARAM_SWRETRY_NOAGG_MAX:
*value = qw->tx_swretry_noagg_max;
break;
case IEEE80211_PARAM_SWRETRY_SUSPEND_XMIT:
*value = qw->tx_swretry_suspend_xmit;
break;
case IEEE80211_PARAM_RX_AGG_TIMEOUT:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"IEEE80211_PARAM_RX_AGG_TIMEOUT (%d)\n", param);
*value = qdrv_wlan_80211_get_rx_agg_timeout(ni);
break;
case IEEE80211_PARAM_CONFIG_TXPOWER:
*value = qdrv_is_gain_low();
break;
case IEEE80211_PARAM_LEGACY_RETRY_LIMIT:
*value = qdrv_wlan_80211_get_legacy_retry_limit(ni);
break;
case IEEE80211_PARAM_MIMOMODE:
*value = qw->tx_mimomode;
break;
case IEEE80211_PARAM_SHORT_RETRY_LIMIT:
case IEEE80211_PARAM_LONG_RETRY_LIMIT:
//Just return max software retry for aggregation.
*value = QTN_TX_SW_ATTEMPTS_AGG_MAX;
break;
case IEEE80211_PARAM_RETRY_COUNT:
*value = qdrv_wlan_80211_get_retry_count(ni);
break;
case IEEE80211_PARAM_BR_IP_ADDR:
qdrv_get_br_ipaddr(qw, (__be32 *)value);
break;
case IEEE80211_PARAM_CACSTATUS:
*value = (qw->sm_stats.sm_state & QDRV_WLAN_SM_STATE_CAC_ACTIVE) ? 1 : 0;
break;
case IEEE80211_PARAM_CARRIER_ID:
*value = g_carrier_id;
break;
case IEEE80211_PARAM_TX_QUEUING_ALG:
*value = qw->tx_sch_shared_data->queuing_alg;
break;
case IEEE80211_PARAM_MODE:
*value = qdrv_wlan_80211_get_11ac_mode(ic);
break;
case IEEE80211_PARAM_CONGEST_IDX:
*value = qdrv_wlan_get_congestion_index(qw);
break;
case IEEE80211_PARAM_MICHAEL_ERR_CNT:
*value = qdrv_wlan_get_michael_errcnt(qw);
break;
case IEEE80211_PARAM_MAX_AGG_SIZE:
*value = ic->ic_tx_max_ampdu_size;
break;
case IEEE80211_PARAM_GET_MU_GRP:
qdrv_get_mu_grp(ni, (void*)data);
break;
case IEEE80211_PARAM_MU_ENABLE:
*value = qdrv_get_mu_enable(ni);
break;
case IEEE80211_PARAM_GET_MU_GRP_QMAT:
*value = qdrv_get_mu_grp_qmat(ni, (*value) >> 16);
break;
case IEEE80211_PARAM_MU_USE_EQ:
*value = qdrv_get_mu_use_eq(ni);
break;
case IEEE80211_PARAM_RESTRICT_WLAN_IP:
*value = qw->restrict_wlan_ip;
break;
case IEEE80211_PARAM_EP_STATUS:
*value = keep_alive_cnt++;
break;
case IEEE80211_PARAM_CCA_FIXED:
*value = ic->cca_fix_disable;
break;
case IEEE80211_PARAM_AUTO_CCA_ENABLE:
*value = ic->auto_cca_enable;
break;
case IEEE80211_IOCTL_GETKEY:
if (ni->ni_vap->iv_bss->ni_rsn.rsn_mcastcipher ==
IEEE80211_CIPHER_AES_CCM)
*value = qw->tx_stats.tx_copy_mc_enc;
else
/* In case of WEP rsc value is '0'*/
*value = 0;
break;
case IEEE80211_PARAM_GET_CCA_STATS:
/* 4-byte value field can accomodate more CCA stats if required */
*value = ic->ic_muc_tx_stats->cca_sec40;
break;
case IEEE80211_PARAM_BEACON_HANG_TIMEOUT:
*value = ic->ic_bcn_hang_timeout;
break;
case IEEE80211_PARAM_BB_DEAFNESS_WAR_EN:
*value = !!ic->bb_deafness_war_disable;
break;
default:
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_WLAN,
"<0x%08x> (%d)\n", param, param);
break;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
void qdrv_wlan_80211_stats(struct ieee80211com *ic, struct iw_statistics *is)
{
struct qdrv_wlan *qw;
struct qdrv_mac *mac;
struct qtn_stats_log *iw_stats_log;
int curr_index;
int rssi;
int noise;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/*
* NB: the referenced node pointer is in the
* control block of the sk_buff. This is
* placed there by ieee80211_mgmt_output because
* we need to hold the reference with the frame.
*/
qw = container_of(ic, struct qdrv_wlan, ic);
mac = qw->mac;
/* Get the data from MuC */
iw_stats_log = (struct qtn_stats_log *)mac->mac_sys_stats;
if (iw_stats_log == NULL) {
/* No stats available from MuC, mark all as invalid */
is->qual.qual = 0;
is->qual.noise = 0;
is->qual.level = 0;
is->qual.updated = IW_QUAL_ALL_INVALID;
return;
}
curr_index = iw_stats_log->curr_buff;
/* Take the previous value */
curr_index = (curr_index - 1 + NUM_LOG_BUFFS)%NUM_LOG_BUFFS;
/* Collect error stats */
is->discard.misc += iw_stats_log->stat_buffs[curr_index].rx_phy_stats.cnt_mac_crc;
is->discard.retries += iw_stats_log->stat_buffs[curr_index].tx_phy_stats.num_retries;
/*
* Collect PHY Stats
*
* The RSSI values reported in the TX/RX descriptors in the driver are the SNR
* expressed in dBm. Thus 'rssi' is signal level above the noise floor in dBm.
*
* Noise is measured in dBm and is negative unless there is an unimaginable
* level of RF noise.
*
* The signal level is noise + rssi.
*
* Note that the iw_quality values are 1 byte, and can be signed, unsigned or
* negative depending on context.
*
* Note iwconfig's quality parameter is a relative value while rssi here is a
* absolute/dBm value.
* Convert rssi from absolute/dBm value to a relative one.
* The convertion logic is reused from qcsapi
*/
rssi = iw_stats_log->stat_buffs[curr_index].rx_phy_stats.last_rssi_all;
noise = iw_stats_log->stat_buffs[curr_index].rx_phy_stats.hw_noise;
if (rssi < 0)
is->qual.level = (rssi - 5) / 10;
else
is->qual.level = (rssi + 5) / 10;
rssi += RSSI_OFFSET_FROM_10THS_DBM;
if (rssi < 0 || rssi >= RSSI_OFFSET_FROM_10THS_DBM)
is->qual.qual = 0;
else
is->qual.qual = (rssi + 5) / 10;
if (noise < 0)
is->qual.noise = (noise - 5) / 10;
else
is->qual.noise = (noise + 5) / 10;
is->qual.updated = IW_QUAL_ALL_UPDATED;
is->qual.updated |= IW_QUAL_DBM;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return;
}
/*
* MIMO power save mode change.
*/
static void qdrv_wlan_80211_smps(struct ieee80211_node *ni, int new_mode)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE,
"Node %02x:%02x:%02x:%02x:%02x:%02x MIMO PS change to %02X"
" for BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
DBGMACFMT(ni->ni_macaddr),
(u_int8_t)new_mode,
DBGMACFMT(ni->ni_bssid));
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate SMPS message\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
/* BSSID for the node and it's MAC address */
memcpy(args->ni_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN);
memcpy(args->ni_macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN);
ioctl->ioctl_command = IOCTL_DEV_SMPS;
ioctl->ioctl_arg1 = qv->devid;
/* new_mode is one of the enumerations starting 'IEEE80211_HTCAP_C_MIMOPWRSAVE_...' */
ioctl->ioctl_arg2 = new_mode;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
static void qdrv_qvsp_node_auth_state_change(struct ieee80211_node *ni, int auth)
{
#ifdef CONFIG_QVSP
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
if (!auth) {
qvsp_node_del(qw->qvsp, ni);
}
#endif
}
static void qdrv_wlan_new_assoc(struct ieee80211_node *ni)
{
#ifdef CONFIG_QVSP
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qvsp_reset(qw->qvsp);
#endif
}
static void qdrv_wlan_auth_state_change(struct ieee80211_node *ni, int auth)
{
const struct ieee80211_node *ni_iter;
const struct ieee80211_node *ni_iter_tmp;
unsigned long flags;
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
int is_ap = 0;
if (IEEE80211_NODE_AID(ni) == 0) {
return;
}
qdrv_qvsp_node_auth_state_change(ni, auth);
is_ap = (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP);
spin_lock_irqsave(&qv->ni_lst_lock, flags);
if (auth) {
qw->sm_stats.sm_nd_auth++;
qw->sm_stats.sm_nd_auth_tot++;
/* List of bridge clients */
if (ni->ni_qtn_assoc_ie && !ni->is_in_bridge_lst) {
TAILQ_INSERT_HEAD(&qv->ni_bridge_lst, ni, ni_bridge_lst);
qv->ni_bridge_cnt++;
ni->is_in_bridge_lst = 1;
}
/* List of Quantenna bridge clients that support 4-addr LNCB reception */
if (ni->ni_lncb_4addr && (!ni->is_in_lncb_lst)) {
TAILQ_INSERT_HEAD(&qv->ni_lncb_lst, ni, ni_lncb_lst);
qv->ni_lncb_cnt++;
ni->is_in_lncb_lst = 1;
} else {
/* Don't count this STA more than once. Can happen when reauthenticating */
if (!ni->ni_in_auth_state && is_ap) {
qv->iv_3addr_count++;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"3 address STA auth'd (count %d)\n",
qv->iv_3addr_count);
}
}
ni->ni_in_auth_state = 1;
} else {
if (ni->ni_in_auth_state && is_ap) {
qw->sm_stats.sm_nd_unauth++;
qw->sm_stats.sm_nd_auth_tot--;
}
if (ni->ni_node_idx)
qdrv_remove_invalid_sub_port(ni->ni_vap, ni->ni_node_idx);
if (ni->ni_qtn_assoc_ie) {
TAILQ_FOREACH_SAFE(ni_iter, &qv->ni_bridge_lst, ni_bridge_lst, ni_iter_tmp) {
if (ni == ni_iter) {
TAILQ_REMOVE(&qv->ni_bridge_lst, ni, ni_bridge_lst);
qv->ni_bridge_cnt--;
ni->is_in_bridge_lst = 0;
KASSERT((qv->ni_bridge_cnt >= 0),
("Negative bridge station count"));
break;
}
}
}
if (ni->ni_lncb_4addr) {
TAILQ_FOREACH_SAFE(ni_iter, &qv->ni_lncb_lst, ni_lncb_lst, ni_iter_tmp) {
if (ni == ni_iter) {
TAILQ_REMOVE(&qv->ni_lncb_lst, ni, ni_lncb_lst);
qv->ni_lncb_cnt--;
ni->is_in_lncb_lst = 0;
KASSERT((qv->ni_lncb_cnt >= 0),
("Negative lncb station count"));
break;
}
}
} else {
if (ni->ni_in_auth_state && is_ap) {
qv->iv_3addr_count--;
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_WLAN,
"3 address STA deauth'd (count %d)\n",
qv->iv_3addr_count);
KASSERT((qv->iv_3addr_count >= 0),
("Negative 3 address count"));
} else {
ni->ni_in_auth_state = 0;
}
}
ni->ni_in_auth_state = 0;
}
spin_unlock_irqrestore(&qv->ni_lst_lock, flags);
}
#define QDRV_BOOTCFG_BUF_LEN 32
enum hw_opt_t get_bootcfg_bond_opt(void)
{
uint32_t bond_opt;
char buf[QDRV_BOOTCFG_BUF_LEN];
char *s;
int rc;
s = bootcfg_get_var("bond_opt", buf);
if (s) {
rc = sscanf(s, "=%d", &bond_opt);
if ((rc == 1) && bond_opt >= 0)
return (bond_opt | HW_OPTION_BONDING_TOPAZ_PROD);
}
return HW_OPTION_BONDING_NOT_SET;
}
static int qdrv_wlan_80211_mark_dfs(struct ieee80211com *ic, int nchans,
struct ieee80211_channel *chans)
{
int i;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
/* check if channel requires DFS */
for (i = 0; i < nchans; i++) {
if (ic->ic_country_code != CTRY_DEFAULT &&
chans[i].ic_ieee > 0 &&
chans[i].ic_ieee < IEEE80211_CHAN_MAX &&
isset(ic->ic_chan_dfs_required, chans[i].ic_ieee)) {
chans[i].ic_flags |= IEEE80211_CHAN_DFS;
/* active scan not allowed on DFS channel */
chans[i].ic_flags |= IEEE80211_CHAN_PASSIVE;
if (vap->iv_opmode == IEEE80211_M_STA) {
ic->ic_chan_availability_status[chans[i].ic_ieee]
= IEEE80211_CHANNEL_STATUS_NON_AVAILABLE;
} else {
ic->ic_chan_availability_status[chans[i].ic_ieee]
= IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_CAC_REQUIRED;
}
if (ic->ic_mark_channel_dfs_cac_status) {
ic->ic_mark_channel_dfs_cac_status(ic, &chans[i], IEEE80211_CHAN_DFS_CAC_DONE, false);
ic->ic_mark_channel_dfs_cac_status(ic, &chans[i], IEEE80211_CHAN_DFS_CAC_IN_PROGRESS, false);
}
}
chans[i].ic_radardetected = 0;
}
return 0;
}
static int qdrv_wlan_80211_mark_weather_radar(struct ieee80211com *ic, int nchans,
struct ieee80211_channel *chans)
{
int i;
int chan_sec_ieee;
struct ieee80211_channel *chan_sec;
for (i = 0; i < nchans; i++) {
if (qdrv_dfs_is_eu_region() &&
chans[i].ic_ieee > 0 &&
chans[i].ic_ieee < IEEE80211_CHAN_MAX &&
isset(ic->ic_chan_weather_radar, chans[i].ic_ieee)) {
chans[i].ic_flags |= IEEE80211_CHAN_WEATHER;
chans[i].ic_flags |= IEEE80211_CHAN_WEATHER_40M;
chans[i].ic_flags |= IEEE80211_CHAN_WEATHER_80M;
chan_sec_ieee = ieee80211_find_sec_chan(&chans[i]);
if (chan_sec_ieee)
chan_sec = ieee80211_find_channel_by_ieee(ic, chan_sec_ieee);
else
chan_sec = NULL;
if (chan_sec)
chan_sec->ic_flags |= IEEE80211_CHAN_WEATHER_40M;
chan_sec_ieee = ieee80211_find_sec40u_chan(&chans[i]);
if (chan_sec_ieee)
chan_sec = ieee80211_find_channel_by_ieee(ic, chan_sec_ieee);
else
chan_sec = NULL;
if (chan_sec)
chan_sec->ic_flags |= IEEE80211_CHAN_WEATHER_80M;
chan_sec_ieee = ieee80211_find_sec40l_chan(&chans[i]);
if (chan_sec_ieee)
chan_sec = ieee80211_find_channel_by_ieee(ic, chan_sec_ieee);
else
chan_sec = NULL;
if (chan_sec)
chan_sec->ic_flags |= IEEE80211_CHAN_WEATHER_80M;
}
}
return 0;
}
static int qdrv_pm_notify(struct notifier_block *b, unsigned long level, void *v)
{
int retval = NOTIFY_OK;
static int pm_prev_level = BOARD_PM_LEVEL_NO;
const int switch_level = BOARD_PM_LEVEL_DUTY;
u_int16_t new_beacon_interval;
struct qdrv_wlan *qw = container_of(b, struct qdrv_wlan, pm_notifier);
struct ieee80211com *ic = &qw->ic;
ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] = level;
#ifdef CONFIG_QVSP
qdrv_wlan_notify_qvsp_coc_state_changed(qw->qvsp, ic);
#endif
qdrv_wlan_notify_pm_state_changed(ic, pm_prev_level);
if ((pm_prev_level < switch_level) && (level >= switch_level)) {
#if defined(QBMPS_ENABLE)
/* qdrv_pm_notify is registered in qos_pm framework */
/* it could be triggered from modules besides wlan qdrv: e.g. qpm */
/* so we need to make sure BMPS is enabled */
/* before going into power-saving in STA mode */
if ((ic->ic_opmode == IEEE80211_M_STA) &&
!(ic->ic_flags_qtn & IEEE80211_QTN_BMPS))
return retval;
#endif
#if defined(QBMPS_ENABLE)
if (ic->ic_opmode != IEEE80211_M_STA) {
#endif
/* BMPS power-saving is used for STA */
/* this is only needed for CoC power-saving in non-STA mode */
new_beacon_interval = ieee80211_pm_period_tu(ic);
if (ic->ic_lintval != new_beacon_interval) {
/* Configure beacon interval to power duty interval */
ieee80211_beacon_interval_set(ic, new_beacon_interval);
}
ic->ic_pm_period_change.expires = jiffies +
ic->ic_pm_state[QTN_PM_PERIOD_CHANGE_INTERVAL] * HZ;
if (&ic->ic_pm_period_change)
add_timer(&ic->ic_pm_period_change);
#if defined(QBMPS_ENABLE)
}
#endif
retval = ((qdrv_hostlink_power_save(qw, QTN_PM_CURRENT_LEVEL, level) < 0) ?
NOTIFY_STOP : NOTIFY_OK);
if ((retval != NOTIFY_OK)
#if defined(QBMPS_ENABLE)
&& (ic->ic_opmode != IEEE80211_M_STA)
#endif
) {
del_timer(&ic->ic_pm_period_change);
ieee80211_beacon_interval_set(ic, ic->ic_lintval_backup);
}
} else if ((pm_prev_level >= switch_level) && (level < switch_level)) {
if (ic->ic_lintval != ic->ic_lintval_backup) {
/* Recovering beacon setting */
ieee80211_beacon_interval_set(ic, ic->ic_lintval_backup);
}
ic->ic_pm_enabled = 1;
retval = ((qdrv_hostlink_power_save(qw, QTN_PM_CURRENT_LEVEL, level) < 0) ?
NOTIFY_STOP : NOTIFY_OK);
ic->ic_pm_enabled = 0;
if ((retval == NOTIFY_OK)
#if defined(QBMPS_ENABLE)
&& (ic->ic_opmode != IEEE80211_M_STA)
#endif
) {
if (&ic->ic_pm_period_change)
del_timer(&ic->ic_pm_period_change);
}
}
pm_prev_level = level;
return retval;
}
static int qdrv_wlan_80211_config_channel(struct ieee80211com *ic, int ic_nchans)
{
struct ieee80211_channel chans[IEEE80211_MAX_5_GHZ_CHANNELS];
int i;
int nchans = 0;
struct ieee80211_channel *inchans = chans;
struct qtn_channel *qtn_chan_ptr = NULL;
u32 def_chan_flags = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
memset(chans, 0, sizeof(chans));
/* Set up some dummy channels */
#ifdef QDRV_FEATURE_HT
nchans = ic_nchans;
KASSERT((sizeof(chans)/sizeof(chans[0])) >= (IEEE80211_MAX_5_GHZ_CHANNELS),
("Negative config channel array size"));
//TODO: Avinash. BBIC4 is not Simutaneous Dual-Band platform.
#ifdef PEARL_PLATFORM
if (nchans == IEEE80211_MAX_DUAL_CHANNELS) {
/* Dual band. Initialize with all the supported channels */
int j;
nchans = IEEE80211_MAX_2_4_GHZ_CHANNELS;
def_chan_flags = IEEE80211_CHAN_HT20 | IEEE80211_CHAN_OFDM |
IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK;
qtn_chan_ptr = qtn_channels_2ghz;
for (i = 0; i < nchans; i++) {
chans[i].ic_flags = def_chan_flags | qtn_chan_ptr[i].channel_flags;
/* Add the common 40M flag if either U/D 20M flag is set */
chans[i].ic_flags |=
(chans[i].ic_flags & (IEEE80211_CHAN_HT40U|IEEE80211_CHAN_HT40D))?
IEEE80211_CHAN_HT40:0;
chans[i].ic_ext_flags = qtn_chan_ptr[i].channel_ext_flags;
chans[i].ic_freq = qtn_chan_ptr[i].channel_freq;
chans[i].ic_ieee = qtn_chan_ptr[i].channel_number;
chans[i].ic_maxregpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_maxpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower = QDRV_DFLT_MIN_TXPOW;
chans[i].ic_maxpower_normal = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower_normal = QDRV_DFLT_MIN_TXPOW;
/* '0' means power is not configured */
memset(&chans[i].ic_maxpower_table, 0, sizeof(chans[i].ic_maxpower_table));
chans[i].ic_center_f_40MHz = qtn_chan_ptr[i].center_freq_40M;
}
nchans = IEEE80211_MAX_5_GHZ_CHANNELS;
def_chan_flags = IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40 |
IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
qtn_chan_ptr = qtn_channels_5ghz;
for (j = 0; j < nchans; j++, i++) {
chans[i].ic_flags = def_chan_flags | qtn_chan_ptr[j].channel_flags;
/* Add the common 40M flag if either U/D 20M flag is set */
chans[i].ic_flags |=
(chans[i].ic_flags & (IEEE80211_CHAN_HT40U|IEEE80211_CHAN_HT40D))?
IEEE80211_CHAN_HT40:0;
chans[i].ic_ext_flags = qtn_chan_ptr[j].channel_ext_flags;
chans[i].ic_freq = qtn_chan_ptr[j].channel_freq;
chans[i].ic_ieee = qtn_chan_ptr[j].channel_number;
chans[i].ic_maxregpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_maxpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower = QDRV_DFLT_MIN_TXPOW;
chans[i].ic_maxpower_normal = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower_normal = QDRV_DFLT_MIN_TXPOW;
/* '0' means power is not configured */
memset(&chans[i].ic_maxpower_table, 0, sizeof(chans[i].ic_maxpower_table));
chans[i].ic_center_f_40MHz = qtn_chan_ptr[j].center_freq_40M;
chans[i].ic_center_f_80MHz = qtn_chan_ptr[j].center_freq_80M;
chans[i].ic_center_f_160MHz = qtn_chan_ptr[j].center_freq_160M;
if (chans[i].ic_center_f_80MHz) {
chans[i].ic_flags |= IEEE80211_CHAN_VHT80;
}
}
nchans = IEEE80211_MAX_DUAL_CHANNELS;
} else {
#endif
if (nchans == IEEE80211_MAX_2_4_GHZ_CHANNELS) {
def_chan_flags = IEEE80211_CHAN_HT20 | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
qtn_chan_ptr = qtn_channels_2ghz;
} else if (nchans == IEEE80211_MAX_5_GHZ_CHANNELS) {
nchans = IEEE80211_MAX_5_GHZ_CHANNELS;
def_chan_flags = IEEE80211_CHAN_HT20 | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
qtn_chan_ptr = qtn_channels_5ghz;
} else {
printk(KERN_ERR "Num of chans specified does not correspond to any known freq band\n");
return -1;
}
for (i = 0; i < nchans; i++) {
chans[i].ic_flags = def_chan_flags | qtn_chan_ptr[i].channel_flags;
/* Add the common 40M flag if either U/D 20M flag is set */
chans[i].ic_flags |=
(chans[i].ic_flags & (IEEE80211_CHAN_HT40U|IEEE80211_CHAN_HT40D))?
IEEE80211_CHAN_HT40:0;
chans[i].ic_ext_flags = qtn_chan_ptr[i].channel_ext_flags;
chans[i].ic_freq = qtn_chan_ptr[i].channel_freq;
chans[i].ic_ieee = qtn_chan_ptr[i].channel_number;
chans[i].ic_maxregpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_maxpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower = QDRV_DFLT_MIN_TXPOW;
chans[i].ic_maxpower_normal = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower_normal = QDRV_DFLT_MIN_TXPOW;
/* '0' means power is not configured */
memset(chans[i].ic_maxpower_table, 0, sizeof(chans[i].ic_maxpower_table));
chans[i].ic_center_f_80MHz = qtn_chan_ptr[i].center_freq_80M;
chans[i].ic_center_f_160MHz = qtn_chan_ptr[i].center_freq_160M;
if (chans[i].ic_center_f_80MHz) {
chans[i].ic_flags |= IEEE80211_CHAN_VHT80;
}
}
#ifdef PEARL_PLATFORM
}
#endif
#else
for (i = 0; i < 32; i++) {
chans[i].ic_freq = 2412 + i;
chans[i].ic_flags = IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
chans[i].ic_ieee = i;
chans[i].ic_maxregpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_maxpower = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower = QDRV_DFLT_MIN_TXPOW;
chans[i].ic_maxpower_normal = QDRV_DFLT_MAX_TXPOW;
chans[i].ic_minpower_normal = QDRV_DFLT_MIN_TXPOW;
}
nchans = ((chans[0].ic_flags & IEEE80211_CHAN_2GHZ) == IEEE80211_CHAN_2GHZ) ?
IEEE80211_MAX_2_4_GHZ_CHANNELS : IEEE80211_MAX_5_GHZ_CHANNELS;
#endif
ic->ic_pwr_constraint = 0;
/* check if channel requires DFS */
qdrv_wlan_80211_mark_dfs(ic, nchans, inchans);
qdrv_wlan_80211_mark_weather_radar(ic, nchans, inchans);
/* Initialize the channels in the ieee80211com structure */
set_channels(ic, nchans, chans);
return 0;
}
#ifdef CONFIG_QVSP
static void
qdrv_wlan_vsp_strm_state_set(struct ieee80211com *ic, uint8_t strm_state,
const struct ieee80211_qvsp_strm_id *strm_id,
struct ieee80211_qvsp_strm_dis_attr *attr)
{
#if !TOPAZ_QTM /* Disable STA side control for QTM-Lite */
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qvsp_s *qvsp = qw->qvsp;
if (qvsp == NULL) {
return;
}
qvsp_cmd_strm_state_set(qvsp, strm_state, strm_id, attr);
#endif
}
static void
qdrv_wlan_vsp_change_stamode(struct ieee80211com *ic, uint8_t stamode)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qvsp_s *qvsp = qw->qvsp;
if (qvsp == NULL) {
return;
}
qvsp_change_stamode(qvsp, stamode);
}
static void
qdrv_wlan_vsp_configure(struct ieee80211com *ic, uint32_t index, uint32_t value)
{
#if !TOPAZ_QTM /* Disable STA side control for QTM-Lite */
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qvsp_s *qvsp = qw->qvsp;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "configuring VSP %u:%u\n",
index, value);
qvsp_cmd_vsp_configure(qvsp, index, value);
#endif
}
static void
qdrv_wlan_vsp_set(struct ieee80211com *ic, uint32_t index, uint32_t value)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qvsp_s *qvsp = qw->qvsp;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "configuring VSP %u:%u\n",
index, value);
qvsp_cmd_vsp_cfg_set(qvsp, index, value);
}
static int
qdrv_wlan_vsp_get(struct ieee80211com *ic, uint32_t index, uint32_t *value)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qvsp_s *qvsp = qw->qvsp;
int ret;
ret = qvsp_cmd_vsp_cfg_get(qvsp, index, value);
if (!ret) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "VSP configuration %u:%u\n",
index, *value);
} else {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "reading VSP failed\n");
}
return ret;
}
int
qdrv_wlan_query_wds(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode == IEEE80211_M_WDS)
return 1;
}
return 0;
}
/*
* VSP config callback to send a configuration updates to peer stations
*/
void
qdrv_wlan_vsp_cb_cfg(void *token, uint32_t index, uint32_t value)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)token;
struct ieee80211com *ic = &qw->ic;
struct ieee80211_qvsp_act_cfg qvsp_ac;
struct ieee80211_action_data act;
uint8_t *oui;
memset(&act, 0, sizeof(act));
act.cat = IEEE80211_ACTION_CAT_VENDOR;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "VSP: send config to stations - %d:%d\n",
index, value);
memset(&qvsp_ac, 0, sizeof(qvsp_ac));
act.params = (void *)&qvsp_ac;
oui = qvsp_ac.header.oui;
ieee80211_oui_add_qtn(oui);
qvsp_ac.header.type = QVSP_ACTION_VSP_CTRL;
qvsp_ac.count = 1;
qvsp_ac.cfg_items[0].index = index;
qvsp_ac.cfg_items[0].value = value;
ieee80211_iterate_nodes(&ic->ic_sta, ieee80211_node_vsp_send_action, &act, 1);
/* Store config for sending to new stations when they associate */
ic->vsp_cfg[index].value = value;
ic->vsp_cfg[index].set = 1;
}
/*
* VSP config callback to send stream state changes to peer stations
*/
void
qdrv_wlan_vsp_cb_strm_ctrl(void *token, struct ieee80211_node *ni, uint8_t strm_state,
struct ieee80211_qvsp_strm_id *strm_id, struct ieee80211_qvsp_strm_dis_attr *attr)
{
struct ieee80211_qvsp_act_strm_ctrl qvsp_ac;
struct ieee80211_action_data act;
uint8_t *oui;
memset(&act, 0, sizeof(act));
act.cat = IEEE80211_ACTION_CAT_VENDOR;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP,
"VSP: send stream state change (%u) to " DBGMACVAR "\n",
strm_state, DBGMACFMT(ni->ni_macaddr));
memset(&qvsp_ac, 0, sizeof(qvsp_ac));
act.params = (void *)&qvsp_ac;
oui = qvsp_ac.header.oui;
ieee80211_oui_add_qtn(oui);
qvsp_ac.header.type = QVSP_ACTION_STRM_CTRL;
qvsp_ac.strm_state = strm_state;
memcpy(&qvsp_ac.dis_attr, attr, sizeof(qvsp_ac.dis_attr));
qvsp_ac.count = 1;
qvsp_ac.strm_items[0] = *strm_id;
ieee80211_node_vsp_send_action(&act, ni);
}
void qdrv_wlan_vsp_reset(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
if (qw->qvsp)
qvsp_reset(qw->qvsp);
}
#if TOPAZ_QTM
static void __sram_text
qdrv_wlan_vsp_sync_node(void *arg, struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct qtn_vsp_stats *vsp_stats = qw->vsp_stats;
struct ieee80211vap *vap = ni->ni_vap;
struct qtn_per_tid_stats *stats;
struct qtn_per_tid_stats *prev_stats;
const uint8_t tids[] = QTN_VSP_TIDS;
uint8_t tid_idx;
uint8_t tid;
const int8_t tid2statsidx[] = QTN_VSP_STATS_TID2IDX;
int8_t stats_idx;
uint8_t node;
uint32_t sent_bytes;
uint32_t sent_pkts;
uint32_t throt_bytes;
uint32_t throt_pkts;
if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_WDS) &&
ni->ni_associd == 0)
return;
node = IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx);
if (!qw->vsp_enabling) {
for (tid_idx = 0; tid_idx < ARRAY_SIZE(tids); ++tid_idx) {
tid = tids[tid_idx];
stats_idx = tid2statsidx[tid];
stats = &vsp_stats->per_node_stats[node].per_tid_stats[stats_idx];
prev_stats = &ni->ni_prev_vsp_stats.per_tid_stats[stats_idx];
/*
* There is a race condition that when lhost is reading these counters from
* shared memory, MuC is updating it. However, this won't hurt VSP. Because
* the key information here is whether throt_pkts is zero or not. We rely
* on this when reenabling streams. The small error in sent_pkts doesn't
* matter. So we don't need to use ping-pong buffer to solve this race condtion.
*/
throt_pkts = stats->tx_throt_pkts - prev_stats->tx_throt_pkts;
throt_bytes = stats->tx_throt_bytes - prev_stats->tx_throt_bytes;
sent_pkts = stats->tx_sent_pkts - prev_stats->tx_sent_pkts;
sent_bytes = stats->tx_sent_bytes - prev_stats->tx_sent_bytes;
qvsp_strm_tid_check_add(qw->qvsp, ni,
IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), tid,
sent_pkts + throt_pkts,
sent_bytes + throt_bytes,
sent_pkts, sent_bytes);
}
}
memcpy(&ni->ni_prev_vsp_stats, &vsp_stats->per_node_stats[node], sizeof(ni->ni_prev_vsp_stats));
}
static void __sram_text
qdrv_wlan_vsp_sync(struct qdrv_wlan *qw)
{
ieee80211_iterate_nodes(&qw->ic.ic_sta, qdrv_wlan_vsp_sync_node, 0, 1);
if (qw->vsp_sync_sched_remain) {
schedule_delayed_work(&qw->vsp_sync_work, HZ);
qw->vsp_sync_sched_remain--;
}
}
static void __sram_text
qdrv_wlan_vsp_sync_work(struct work_struct *work)
{
struct delayed_work *dwork = (struct delayed_work *)work;
struct qdrv_wlan *qw = container_of(dwork, struct qdrv_wlan, vsp_sync_work);
qdrv_wlan_vsp_sync(qw);
}
#endif
static void __sram_text
qdrv_wlan_vsp_tasklet(unsigned long _qw)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) _qw;
if (likely(qvsp_is_active(qw->qvsp))) {
#if TOPAZ_QTM
/* sched work one time less than interval number because here we already do one */
qw->vsp_sync_sched_remain = qw->vsp_check_intvl - 1;
qdrv_wlan_vsp_sync(qw);
if (qw->vsp_enabling) {
/* warming up, stats not ready yet */
qw->vsp_enabling--;
return;
}
#endif
qvsp_fat_set(qw->qvsp,
qw->vsp_stats->fat, qw->vsp_stats->intf_ms,
qw->ic.ic_curchan->ic_ieee);
}
}
static void __sram_text
qdrv_wlan_vsp_irq_handler(void *_qw, void *_unused)
{
struct qdrv_wlan *qw = _qw;
if (likely(qvsp_is_active(qw->qvsp))) {
tasklet_schedule(&qw->vsp_tasklet);
}
}
void qdrv_wlan_vsp_cb_strm_ext_throttler(void *token, struct ieee80211_node *ni,
uint8_t strm_state, const struct ieee80211_qvsp_strm_id *strm_id,
struct ieee80211_qvsp_strm_dis_attr *attr, uint32_t throt_intvl)
{
#if TOPAZ_QTM
uint8_t node;
uint8_t tid;
uint32_t value;
uint32_t intvl;
uint32_t quota;
qvsp_fake_ip2nodetid((uint32_t*)(&strm_id->daddr.ipv4), &node, &tid);
if (strm_state == QVSP_STRM_STATE_DISABLED) {
/* default interval */
intvl = throt_intvl; /* ms */
quota = (attr->throt_rate / 8) * intvl; /* bytes */
if (quota < QTN_AUC_THROT_QUOTA_UNIT) {
quota = QTN_AUC_THROT_QUOTA_UNIT;
} else if (quota > (QTN_AUC_THROT_QUOTA_MAX * QTN_AUC_THROT_QUOTA_UNIT)) {
quota = QTN_AUC_THROT_QUOTA_MAX * QTN_AUC_THROT_QUOTA_UNIT;
}
intvl = quota / (attr->throt_rate / 8);
if ((intvl < QTN_AUC_THROT_INTVL_UNIT) ||
(intvl > QTN_AUC_THROT_INTVL_MAX * QTN_AUC_THROT_INTVL_UNIT)) {
printk("VSP: throttling rate %u exceeds ioctl range: intvl %u quota %u\n",
attr->throt_rate, intvl, quota);
return;
}
intvl /= QTN_AUC_THROT_INTVL_UNIT;
quota /= QTN_AUC_THROT_QUOTA_UNIT;
} else {
intvl = 0;
quota = 0;
}
value = SM(AUC_QOS_SCH_PARAM_TID_THROT, AUC_QOS_SCH_PARAM) |
SM(node, QTN_AUC_THROT_NODE) |
SM(tid, QTN_AUC_THROT_TID) |
SM(intvl, QTN_AUC_THROT_INTVL) |
SM(quota, QTN_AUC_THROT_QUOTA);
qdrv_wlan_80211_setparam(ni, IEEE80211_PARAM_AUC_QOS_SCH, value, NULL, 0);
#endif
}
static int
qdrv_wlan_vsp_irq_init(struct qdrv_wlan *qw, unsigned long hi_vsp_stats_phys)
{
struct qdrv_mac *mac = qw->mac;
struct int_handler int_handler;
int ret;
qw->vsp_stats = ioremap_nocache(muc_to_lhost(hi_vsp_stats_phys),
sizeof(*qw->vsp_stats));
if (qw->vsp_stats == NULL) {
return -ENOMEM;
}
tasklet_init(&qw->vsp_tasklet, &qdrv_wlan_vsp_tasklet, (unsigned long) qw);
#if TOPAZ_QTM
INIT_DELAYED_WORK(&qw->vsp_sync_work, qdrv_wlan_vsp_sync_work);
#endif
int_handler.handler = &qdrv_wlan_vsp_irq_handler;
int_handler.arg1 = qw;
int_handler.arg2 = NULL;
ret = qdrv_mac_set_handler(mac, RUBY_M2L_IRQ_LO_VSP, &int_handler);
if (ret == 0) {
qdrv_mac_enable_irq(mac, RUBY_M2L_IRQ_LO_VSP);
} else {
DBGPRINTF_E("Could not initialize VSP update irq handler\n");
iounmap(qw->vsp_stats);
}
return ret;
}
static void
qdrv_wlan_vsp_irq_exit(struct qdrv_wlan *qw)
{
struct qdrv_mac *mac = qw->mac;
iounmap(qw->vsp_stats);
qdrv_mac_disable_irq(mac, RUBY_M2L_IRQ_LO_VSP);
qdrv_mac_clear_handler(mac, RUBY_M2L_IRQ_LO_VSP);
tasklet_kill(&qw->vsp_tasklet);
#if TOPAZ_QTM
cancel_delayed_work_sync(&qw->vsp_sync_work);
#endif
}
int
qdrv_wlan_vsp_ba_throt(struct ieee80211_node *ni, int32_t tid, int intv, int dur, int win_size)
{
struct ieee80211com *ic = ni->ni_ic;
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
struct ieee80211_ba_throt *ba_throt;
int start_timer = 0;
ba_throt = &ni->ni_ba_rx[tid].ba_throt;
if (ba_throt->throt_dur && !dur) {
ic->ic_vsp_ba_throt_num--;
ni->ni_vsp_ba_throt_bm &= ~BIT(tid);
} else if (!ba_throt->throt_dur && dur) {
if (!ic->ic_vsp_ba_throt_num) {
start_timer = 1;
}
ic->ic_vsp_ba_throt_num++;
ni->ni_vsp_ba_throt_bm |= BIT(tid);
}
ba_throt->throt_intv = intv;
ba_throt->throt_dur = dur;
ba_throt->throt_win_size = win_size;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "set node %u tid %d ba throt: intv=%u dur=%u win_size=%u\n",
IEEE80211_AID(ni->ni_associd), tid,
ba_throt->throt_intv,
ba_throt->throt_dur,
ba_throt->throt_win_size);
qdrv_wlan_drop_ba(ni, tid, 0, IEEE80211_REASON_UNSPECIFIED);
if (start_timer) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "add vsp ba throt timer with intv %u ms\n",
QVSP_BA_THROT_TIMER_INTV);
qw->vsp_ba_throt.expires = jiffies + msecs_to_jiffies(QVSP_BA_THROT_TIMER_INTV);
add_timer(&qw->vsp_ba_throt);
}
return 0;
}
static void
qdrv_wlan_vsp_ba_throt_timer(unsigned long arg)
{
struct qdrv_wlan *qw = (struct qdrv_wlan*)arg;
struct ieee80211com *ic = &qw->ic;
struct ieee80211_node *ni;
struct ieee80211_node_table *nt = &ic->ic_sta;
int32_t tid;
struct ieee80211_ba_tid *ba_tid;
struct ieee80211_ba_throt *ba_throt;
if (!ic->ic_vsp_ba_throt_num) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "auto stop vsp ba throt timer\n");
return;
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_VSP, "vsp ba throt timer\n");
IEEE80211_SCAN_LOCK_BH(nt);
IEEE80211_NODE_LOCK_BH(nt);
TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
if ((ni == ni->ni_vap->iv_bss) ||
IEEE80211_ADDR_EQ(ni->ni_vap->iv_myaddr, ni->ni_macaddr) ||
ieee80211_blacklist_check(ni)) {
continue;
}
if (!ni->ni_vsp_ba_throt_bm) {
continue;
}
for (tid = 0; tid < WME_NUM_TID; tid++) {
ba_tid = &ni->ni_ba_rx[tid];
ba_throt = &ba_tid->ba_throt;
if ((ba_tid->state == IEEE80211_BA_ESTABLISHED) &&
ba_throt->throt_dur &&
time_after(jiffies, (ba_throt->last_setup_jiffies +
msecs_to_jiffies(ba_throt->throt_dur)))) {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP,
"VSP: delba node %u tid %d\n", IEEE80211_AID(ni->ni_associd), tid);
qdrv_wlan_drop_ba(ni, tid, 0, IEEE80211_REASON_UNSPECIFIED);
}
}
}
IEEE80211_NODE_UNLOCK_BH(nt);
IEEE80211_SCAN_UNLOCK_BH(nt);
qw->vsp_ba_throt.expires = jiffies + msecs_to_jiffies(QVSP_BA_THROT_TIMER_INTV);
add_timer(&qw->vsp_ba_throt);
}
struct qvsp_3rdpt_method_entry {
uint8_t vendor;
uint8_t ba_throt_session_dur; /* bool, whether to throt session duration */
uint8_t ba_throt_winsize; /* bool, whether to throt winsize */
};
#define QVSP_3RDPT_VENDOR_METHOD_NUM 8
static struct qvsp_3rdpt_method_entry qvsp_3rdpt_method_table[QVSP_3RDPT_VENDOR_METHOD_NUM] = {
{PEER_VENDOR_NONE, 1, 1}, /* must be first entry */
/* more entries can be dynamically added */
};
int qdrv_wlan_vsp_3rdpt_get_method(struct ieee80211_node *ni, uint8_t *throt_session_dur, uint8_t *throt_winsize)
{
int i;
struct qvsp_3rdpt_method_entry *entry;
for (i = 0; i < ARRAY_SIZE(qvsp_3rdpt_method_table); i++) {
entry = &qvsp_3rdpt_method_table[i];
if (entry->vendor == ni->ni_vendor) {
*throt_session_dur = entry->ba_throt_session_dur;
*throt_winsize = entry->ba_throt_winsize;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "VSP: node %u vendor 0x%x"
" throt_dur=%u throt_winsize=%u\n",
IEEE80211_AID(ni->ni_associd), ni->ni_vendor,
*throt_session_dur, *throt_winsize);
return 1;
}
}
entry = &qvsp_3rdpt_method_table[0];
*throt_session_dur = entry->ba_throt_session_dur;
*throt_winsize = entry->ba_throt_winsize;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "VSP: node %u use default method"
" throt_dur=%u throt_winsize=%u\n",
IEEE80211_AID(ni->ni_associd),
*throt_session_dur, *throt_winsize);
return 1;
}
static void qdrv_wlan_vsp_3rdpt_set_method(uint8_t idx, uint8_t vendor,
uint8_t throt_session_dur, uint8_t throt_winsize)
{
struct qvsp_3rdpt_method_entry *entry;
if (idx < ARRAY_SIZE(qvsp_3rdpt_method_table)) {
entry = &qvsp_3rdpt_method_table[idx];
entry->vendor = vendor;
entry->ba_throt_session_dur = throt_session_dur;
entry->ba_throt_winsize = throt_winsize;
} else {
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_VSP, "invalid table index %u\n", idx);
}
}
static void qdrv_wlan_vsp_3rdpt_dump_method_table(void)
{
struct qvsp_3rdpt_method_entry *entry;
int i;
printk("VSP 3rd party method table:\n");
printk("idx vendor throt_dur throt_winsize\n");
for (i = 0; i < ARRAY_SIZE(qvsp_3rdpt_method_table); i++) {
entry = &qvsp_3rdpt_method_table[i];
printk("%3u 0x%04x %9u %13u\n", i, entry->vendor,
entry->ba_throt_session_dur, entry->ba_throt_winsize);
}
}
enum qdrv_manual_ba_throt_subcmd {
QDRV_MANUAL_BA_THROT_SUBCMD_SET_PARAM = 0,
QDRV_MANUAL_BA_THROT_SUBCMD_APPLY_THROT = 1,
QDRV_MANUAL_BA_THROT_SUBCMD_DUMP_BA = 2,
QDRV_MANUAL_BA_THROT_SUBCMD_SET_VENDOR_TABLE = 3,
};
#define QDRV_MANUAL_BA_THROT_SUBCMD 0xC0000000
#define QDRV_MANUAL_BA_THROT_VALUE 0x3FFFFFFF
#define QDRV_MANUAL_BA_THROT_INTV 0x3FFF0000
#define QDRV_MANUAL_BA_THROT_DUR 0x0000FF00
#define QDRV_MANUAL_BA_THROT_WINSIZE 0x000000FF
#define QDRV_MANUAL_BA_THROT_ENABLE 0x3FFF0000
#define QDRV_MANUAL_BA_THROT_NCIDX 0x0000FF00
#define QDRV_MANUAL_BA_THROT_TID 0x000000FF
#define QDRV_MANUAL_BA_THROT_DUMP_NCIDX 0x000000FF
#define QDRV_MANUAL_BA_THROT_IDX 0x3F000000
#define QDRV_MANUAL_BA_THROT_VENDOR 0x00F00000
#define QDRV_MANUAL_BA_THROT_USE_DUR 0x000F0000
#define QDRV_MANUAL_BA_THROT_USE_WINSIZE 0x0000F000
/*
* Manually control BA throttling instead of VSP automatic control
*/
static void qdrv_wlan_manual_ba_throt(struct qdrv_wlan *qw, struct qdrv_vap *qv, unsigned int value)
{
uint32_t subcmd;
struct ieee80211_node *ni = NULL;
static uint32_t manual_ba_throt_intv;
static uint32_t manual_ba_throt_dur;
static uint32_t manual_ba_throt_winsize;
uint32_t enable;
uint32_t ncidx;
int32_t tid;
uint8_t idx;
uint8_t vendor;
uint8_t use_dur_throt;
uint8_t use_winsize_throt;
subcmd = MS_OP(value, QDRV_MANUAL_BA_THROT_SUBCMD);
value = MS_OP(value, QDRV_MANUAL_BA_THROT_VALUE);
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_VSP, "manual ba throt: subcmd=%u, value=0x%x\n", subcmd, value);
switch (subcmd) {
case QDRV_MANUAL_BA_THROT_SUBCMD_SET_PARAM:
manual_ba_throt_intv = MS_OP(value, QDRV_MANUAL_BA_THROT_INTV);
manual_ba_throt_dur = MS_OP(value, QDRV_MANUAL_BA_THROT_DUR);
manual_ba_throt_winsize = MS_OP(value, QDRV_MANUAL_BA_THROT_WINSIZE);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "set manual ba throt intv=%u dur=%u win_size=%u\n",
manual_ba_throt_intv, manual_ba_throt_dur, manual_ba_throt_winsize);
break;
case QDRV_MANUAL_BA_THROT_SUBCMD_APPLY_THROT:
enable = MS_OP(value, QDRV_MANUAL_BA_THROT_ENABLE);
ncidx = MS_OP(value, QDRV_MANUAL_BA_THROT_NCIDX);
tid = MS_OP(value, QDRV_MANUAL_BA_THROT_TID);
ni = ieee80211_find_node_by_node_idx(&qv->iv, ncidx);
if (!ni) {
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_VSP, "node %u not found\n", ncidx);
break;
}
if (enable) {
qdrv_wlan_vsp_ba_throt(ni, tid, manual_ba_throt_intv, manual_ba_throt_dur,
manual_ba_throt_winsize);
} else {
qdrv_wlan_vsp_ba_throt(ni, tid, 0, 0, 0);
}
ieee80211_free_node(ni);
break;
case QDRV_MANUAL_BA_THROT_SUBCMD_DUMP_BA:
ncidx = MS_OP(value, QDRV_MANUAL_BA_THROT_DUMP_NCIDX);
ni = ieee80211_find_node_by_node_idx(&qv->iv, ncidx);
if (!ni) {
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_VSP, "node %u not found\n", ncidx);
break;
}
qdrv_wlan_dump_ba(ni);
ieee80211_free_node(ni);
break;
case QDRV_MANUAL_BA_THROT_SUBCMD_SET_VENDOR_TABLE:
idx = MS_OP(value, QDRV_MANUAL_BA_THROT_IDX);
vendor = MS_OP(value, QDRV_MANUAL_BA_THROT_VENDOR);
use_dur_throt = MS_OP(value, QDRV_MANUAL_BA_THROT_USE_DUR);
use_winsize_throt = MS_OP(value, QDRV_MANUAL_BA_THROT_USE_WINSIZE);
qdrv_wlan_vsp_3rdpt_set_method(idx, vendor, use_dur_throt, use_winsize_throt);
qdrv_wlan_vsp_3rdpt_dump_method_table();
break;
default:
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "unknown subcmd %u\n", subcmd);
break;
}
}
int qdrv_wlan_vsp_wme_throt(void *token, uint32_t ac, uint32_t enable,
uint32_t aifsn, uint32_t ecwmin, uint32_t ecwmax, uint32_t txoplimit,
uint32_t add_qwme_ie)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)token;
struct ieee80211com *ic = &qw->ic;
struct ieee80211vap *vap;
struct ieee80211_wme_state *wme = &qw->ic.ic_wme;
struct chanAccParams *acc_params;
struct wmm_params *params;
acc_params = &wme->wme_throt_bssChanParams;
params = &acc_params->cap_wmeParams[ac];
if (enable) {
wme->wme_throt_bm |= BIT(ac);
wme->wme_throt_add_qwme_ie = add_qwme_ie;
memcpy(params, &wme->wme_bssChanParams.cap_wmeParams[ac], sizeof(struct wmm_params));
params->wmm_aifsn = aifsn;
params->wmm_logcwmin = ecwmin;
params->wmm_logcwmax = ecwmax;
params->wmm_txopLimit = txoplimit;
} else {
wme->wme_throt_bm &= ~BIT(ac);
if (!wme->wme_throt_bm) {
wme->wme_throt_add_qwme_ie = 0;
}
memset(params, 0x0, sizeof(struct wmm_params));
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "set ac %u wme throt: enable=%u aifsn=%u"
" ecwmin=%u ecwmax=%u txoplimit=%u add_qwme_ie=%u\n",
ac, enable, params->wmm_aifsn, params->wmm_logcwmin, params->wmm_logcwmax,
params->wmm_txopLimit, wme->wme_throt_add_qwme_ie);
wme->wme_wmeBssChanParams.cap_info_count++;
/* apply it to all vap as we don't support per-vap wme params now */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
ieee80211_wme_updateparams(vap, 0);
}
return 0;
}
static void qdrv_wlan_vsp_wme_throt_dump(struct qdrv_wlan *qw)
{
struct ieee80211_wme_state *wme = &qw->ic.ic_wme;
struct chanAccParams *acc_params;
struct wmm_params *params;
uint32_t ac;
acc_params = &wme->wme_throt_bssChanParams;
printk("VSP wme throt state: throt_bm=0x%x, add_qwme_ie=%u\n", wme->wme_throt_bm,
wme->wme_throt_add_qwme_ie);
printk("ac enable aifsn ecwmin ecwmax txoplimit\n");
for (ac = 0; ac < WME_NUM_AC; ac++) {
params = &acc_params->cap_wmeParams[ac];
printk("%2u %6u %5u %6u %6u %9u\n",
ac, !!(wme->wme_throt_bm & BIT(ac)),
params->wmm_aifsn, params->wmm_logcwmin, params->wmm_logcwmax,
params->wmm_txopLimit);
}
}
enum qdrv_manual_wme_throt_subcmd {
QDRV_MANUAL_WME_THROT_SUBCMD_SET_PARAM = 0,
QDRV_MANUAL_WME_THROT_SUBCMD_APPLY_THROT = 1,
QDRV_MANUAL_WME_THROT_SUBCMD_DUMP = 2,
};
#define QDRV_MANUAL_WME_THROT_SUBCMD 0xC0000000
#define QDRV_MANUAL_WME_THROT_VALUE 0x3FFFFFFF
#define QDRV_MANUAL_WME_THROT_AIFSN 0x3F000000
#define QDRV_MANUAL_WME_THROT_ECWMIN 0x00F00000
#define QDRV_MANUAL_WME_THROT_ECWMAX 0x000F0000
#define QDRV_MANUAL_WME_THROT_TXOPLIMIT 0x0000FFFF
#define QDRV_MANUAL_WME_THROT_ENABLE 0x3F000000
#define QDRV_MANUAL_WME_THROT_AC 0x00F00000
/*
* Manually control WME throttling instead of VSP automatic control
*/
static void qdrv_wlan_manual_wme_throt(struct qdrv_wlan *qw, struct qdrv_vap *qv, unsigned int value)
{
uint32_t subcmd;
static uint32_t manual_wme_throt_aifsn;
static uint32_t manual_wme_throt_ecwmin;
static uint32_t manual_wme_throt_ecwmax;
static uint32_t manual_wme_throt_txoplimit;
uint32_t enable;
uint32_t ac;
subcmd = MS_OP(value, QDRV_MANUAL_WME_THROT_SUBCMD);
value = MS_OP(value, QDRV_MANUAL_WME_THROT_VALUE);
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_VSP, "manual wme throt: subcmd=%u, value=0x%x\n", subcmd, value);
switch (subcmd) {
case QDRV_MANUAL_WME_THROT_SUBCMD_SET_PARAM:
manual_wme_throt_aifsn = MS_OP(value, QDRV_MANUAL_WME_THROT_AIFSN);
manual_wme_throt_ecwmin = MS_OP(value, QDRV_MANUAL_WME_THROT_ECWMIN);
manual_wme_throt_ecwmax = MS_OP(value, QDRV_MANUAL_WME_THROT_ECWMAX);
manual_wme_throt_txoplimit = MS_OP(value, QDRV_MANUAL_WME_THROT_TXOPLIMIT);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP,
"set manual wme throt aifsn=%u ecwmin=%u ecwmax=%u txoplimit=%u\n",
manual_wme_throt_aifsn, manual_wme_throt_ecwmin, manual_wme_throt_ecwmax,
manual_wme_throt_txoplimit);
break;
case QDRV_MANUAL_WME_THROT_SUBCMD_APPLY_THROT:
enable = MS_OP(value, QDRV_MANUAL_WME_THROT_ENABLE);
ac = MS_OP(value, QDRV_MANUAL_WME_THROT_AC);
if (enable) {
qdrv_wlan_vsp_wme_throt(qw, ac, enable,
manual_wme_throt_aifsn, manual_wme_throt_ecwmin,
manual_wme_throt_ecwmax, manual_wme_throt_txoplimit, 1);
} else {
qdrv_wlan_vsp_wme_throt(qw, ac, 0, 0, 0, 0, 0, 0);
}
break;
case QDRV_MANUAL_WME_THROT_SUBCMD_DUMP:
qdrv_wlan_vsp_wme_throt_dump(qw);
break;
default:
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "unknown subcmd %u\n", subcmd);
break;
}
}
int qdrv_wlan_vsp_3rdpt_init(struct qdrv_wlan *qw)
{
qvsp_3rdpt_register_cb(qw->qvsp, &qw->ic.ic_wme, qdrv_wlan_vsp_3rdpt_get_method, qdrv_wlan_vsp_ba_throt,
qdrv_wlan_vsp_wme_throt);
init_timer(&qw->vsp_ba_throt);
qw->vsp_ba_throt.function = qdrv_wlan_vsp_ba_throt_timer;
qw->vsp_ba_throt.data = (unsigned long) qw;
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "init ok\n");
return 0;
}
void qdrv_wlan_vsp_3rdpt_exit(struct qdrv_wlan *qw)
{
del_timer(&qw->vsp_ba_throt);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_VSP, "exit ok\n");
}
#endif /* CONFIG_QVSP */
extern void dfs_reentry_chan_switch_notify(struct net_device *dev, struct ieee80211_channel *new_chan);
extern struct ieee80211_channel* qdrv_radar_select_newchan(u_int8_t new_ieee);
static void qdrv_wlan_send_csa_frame(struct ieee80211vap *vap,
u_int8_t csa_mode,
u_int8_t csa_chan,
u_int8_t csa_count,
u_int64_t tsf)
{
if (vap->iv_bss == NULL) {
DBGPRINTF_E("CSA sending frame for NULL BSS\n");
} else {
ieee80211_send_csa_frame(vap, csa_mode, csa_chan, csa_count, tsf);
}
}
static void qdrv_wlan_pm_state_init(struct ieee80211com *ic)
{
const static int defaults[QTN_PM_IOCTL_MAX] = QTN_PM_PARAM_DEFAULTS;
memcpy(ic->ic_pm_state, defaults, sizeof(ic->ic_pm_state));
}
void qdrv_wlan_coex_stats_update(struct ieee80211com *ic, uint32_t value)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
switch (value) {
case WLAN_COEX_STATS_BW_ACTION:
RXSTAT(qw, rx_coex_bw_action);
break;
case WLAN_COEX_STATS_BW_ASSOC:
RXSTAT(qw, rx_coex_bw_assoc);
break;
case WLAN_COEX_STATS_BW_SCAN:
RXSTAT(qw, rx_coex_bw_scan);
break;
}
}
int ieee80211_get_cca_adjusting_status(void)
{
volatile struct shared_params *sp = qtn_mproc_sync_shared_params_get();
return sp->cca_adjusting_flag;
}
static int qdrv_wlan_80211_cfg_ht(struct ieee80211com *ic)
{
#ifdef QDRV_FEATURE_HT
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
ic->ic_htcap.maxmsdu = IEEE80211_MSDU_SIZE_3839;
ic->ic_htcap.cap |= (IEEE80211_HTCAP_C_CHWIDTH40 |
IEEE80211_HTCAP_C_SHORTGI40 |
IEEE80211_HTCAP_C_SHORTGI20);
ic->ic_htcap.numrxstbcstr = IEEE80211_MAX_TX_STBC_SS;
ic->ic_htcap.cap |= (IEEE80211_HTCAP_C_TXSTBC |
IEEE80211_HTCAP_C_RXSTBC |
IEEE80211_HTCAP_C_MAXAMSDUSIZE_8K);
ic->ic_htcap.pwrsave = IEEE80211_HTCAP_C_MIMOPWRSAVE_NONE ;
/*
* Workaround for transfer across slow ethernet interfaces (100Mbps or less)
* Reduce advertised RX MAX AMPDU to reduce sender hold time
* Reduce TX aggr hold time (done in MuC)
*/
if (board_slow_ethernet()) {
ic->ic_htcap.maxampdu = IEEE80211_HTCAP_MAXRXAMPDU_8191;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE,
"Slow Ethernet WAR: RXAMPDU %d bytes\n", ic->ic_htcap.maxampdu);
} else {
ic->ic_htcap.maxampdu = IEEE80211_HTCAP_MAXRXAMPDU_65535;
}
if (sp->lh_chip_id >= QTN_BBIC_11AC) {
ic->ic_htcap.mpduspacing = IEEE80211_HTCAP_MPDUSPACING_4;
} else {
ic->ic_htcap.mpduspacing = IEEE80211_HTCAP_MPDUSPACING_8;
}
ic->ic_htcap.maxdatarate = 0; /* Highest advertised rate is supported */
IEEE80211_HTCAP_SET_TXBF_CAPABILITIES(&ic->ic_htcap,
(IEEE80211_HTCAP_B_NDP_RX |
IEEE80211_HTCAP_B_NDP_TX |
IEEE80211_HTCAP_B_EXP_NCOMP_STEER |
IEEE80211_HTCAP_B_EXP_COMP_STEER));
IEEE80211_HTCAP_SET_EXP_NCOMP_TXBF(&ic->ic_htcap, IEEE80211_HTCAP_B_CAPABLE_BOTH);
IEEE80211_HTCAP_SET_EXP_COMP_TXBF(&ic->ic_htcap, IEEE80211_HTCAP_B_CAPABLE_BOTH);
IEEE80211_HTCAP_SET_GROUPING(&ic->ic_htcap, IEEE80211_HTCAP_B_GROUPING_ONE_TWO_FOUR);
if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0)) {
ic->ic_ht_nss_cap = QTN_2X2_GLOBAL_RATE_NSS_MAX;
IEEE80211_HTCAP_SET_NCOMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_TWO);
IEEE80211_HTCAP_SET_COMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_TWO);
IEEE80211_HTCAP_SET_CHAN_EST(&ic->ic_htcap, IEEE80211_HTCAP_B_ST_STREAM_TWO);
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) {
ic->ic_ht_nss_cap = QTN_3X3_GLOBAL_RATE_NSS_MAX;
IEEE80211_HTCAP_SET_NCOMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_THREE);
IEEE80211_HTCAP_SET_COMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_THREE);
IEEE80211_HTCAP_SET_CHAN_EST(&ic->ic_htcap, IEEE80211_HTCAP_B_ST_STREAM_THREE);
} else {
ic->ic_ht_nss_cap = QTN_GLOBAL_RATE_NSS_MAX;
IEEE80211_HTCAP_SET_NCOMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_FOUR);
IEEE80211_HTCAP_SET_COMP_NUM_BF(&ic->ic_htcap, IEEE80211_HTCAP_B_ANTENNAS_FOUR);
IEEE80211_HTCAP_SET_CHAN_EST(&ic->ic_htcap, IEEE80211_HTCAP_B_ST_STREAM_FOUR);
}
qdrv_wlan_80211_set_mcsset(ic);
qdrv_wlan_80211_set_mcsparams(ic);
ic->ic_htinfo.sigranularity = IEEE80211_HTINFO_SIGRANULARITY_5;
ic->ic_htinfo.basicmcsset[IEEE80211_HT_MCSSET_20_40_NSS1] = 0;
ic->ic_htinfo.basicmcsset[IEEE80211_HT_MCSSET_20_40_NSS2] = 0;
#endif
return 0;
}
static int qdrv_wlan_80211_cfg_vht(struct ieee80211_vhtcap *vhtcap, struct ieee80211_vhtop *vhtop,
enum ieee80211_vht_nss *vht_nss_cap, int band_24g, enum ieee80211_opmode opmode,
uint8_t mu_enable)
{
#ifdef QDRV_FEATURE_VHT
/*
* Not yet supported:
* IEEE80211_VHTCAP_C_SHORT_GI_160
* IEEE80211_VHTCAP_C_VHT_TXOP_PS
*/
vhtcap->cap_flags = IEEE80211_VHTCAP_C_RX_LDPC |
IEEE80211_VHTCAP_C_SHORT_GI_80 |
IEEE80211_VHTCAP_C_TX_STBC |
IEEE80211_VHTCAP_C_SU_BEAM_FORMER_CAP |
IEEE80211_VHTCAP_C_SU_BEAM_FORMEE_CAP |
IEEE80211_VHTCAP_C_PLUS_HTC_MINUS_VHT_CAP |
IEEE80211_VHTCAP_C_RX_ATN_PATTERN_CONSISTNCY |
IEEE80211_VHTCAP_C_TX_ATN_PATTERN_CONSISTNCY;
if (mu_enable) {
if (opmode == IEEE80211_M_STA) {
vhtcap->cap_flags |= IEEE80211_VHTCAP_C_MU_BEAM_FORMEE_CAP;
} else if (opmode == IEEE80211_M_HOSTAP) {
vhtcap->cap_flags |= IEEE80211_VHTCAP_C_MU_BEAM_FORMER_CAP;
}
}
vhtcap->maxmpdu = IEEE80211_VHTCAP_MAX_MPDU_11454;
vhtcap->chanwidth = IEEE80211_VHTCAP_CW_80M_ONLY ;
vhtcap->rxstbc = IEEE80211_VHTCAP_RX_STBC_UPTO_1;
vhtcap->maxampduexp = (band_24g ? IEEE80211_VHTCAP_MAX_A_MPDU_65535 : IEEE80211_VHTCAP_MAX_A_MPDU_1048575); /* revisit */
vhtcap->lnkadptcap = IEEE80211_VHTCAP_LNKADAPTCAP_BOTH;
if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0)) {
*vht_nss_cap = IEEE80211_VHT_NSS2;
vhtcap->bfstscap = IEEE80211_VHTCAP_RX_STS_2;
vhtcap->numsounding = IEEE80211_VHTCAP_SNDDIM_2;
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) {
*vht_nss_cap = IEEE80211_VHT_NSS4;
vhtcap->bfstscap = IEEE80211_VHTCAP_RX_STS_4;
vhtcap->numsounding = IEEE80211_VHTCAP_SNDDIM_2;
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) {
*vht_nss_cap = IEEE80211_VHT_NSS3;
vhtcap->bfstscap = IEEE80211_VHTCAP_RX_STS_3;
vhtcap->numsounding = IEEE80211_VHTCAP_SNDDIM_3;
} else if (ieee80211_swfeat_is_supported(SWFEAT_ID_4X4, 0)) {
*vht_nss_cap = IEEE80211_VHT_NSS4;
vhtcap->bfstscap = IEEE80211_VHTCAP_RX_STS_4;
vhtcap->numsounding = IEEE80211_VHTCAP_SNDDIM_4;
} else {
DBGPRINTF_E("%s: stream mode is not valid\n", __func__);
return -1;
}
vhtcap->bfstscap_save = IEEE80211_VHTCAP_RX_STS_INVALID;
qdrv_wlan_80211_set_vht_mcsset(vhtcap, *vht_nss_cap, IEEE80211_VHT_MCS_0_9);
vhtcap->rxlgimaxrate = 0; /* revisit */
vhtcap->txlgimaxrate = 0; /* revisit */
vhtop->chanwidth = (band_24g ? IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ : IEEE80211_VHTOP_CHAN_WIDTH_80MHZ);
vhtop->centerfreq0 = 0; /* revisit */
vhtop->centerfreq1 = 0; /* Not supported in current BBIC4 hardware */
vhtop->basicvhtmcsnssset = htons(qdrv_wlan_80211_vhtmcs_map(IEEE80211_VHT_NSS1,
IEEE80211_VHT_MCS_0_7));
#endif /* QDRV_FEATURE_VHT */
return 0;
}
static void qdrv_wlan_init_dm_factors(struct ieee80211com *ic)
{
char tmpbuf[QDRV_BOOTCFG_BUF_LEN];
char *varstart;
int value = 0;
ic->ic_dm_factor.flags = 0;
varstart = bootcfg_get_var("dm_txpower_factor", tmpbuf);
if (varstart != NULL &&
sscanf(varstart, "=%d", &value) == 1) {
if (value >= DM_TXPOWER_FACTOR_MIN &&
value <= DM_TXPOWER_FACTOR_MAX) {
ic->ic_dm_factor.flags |= DM_FLAG_TXPOWER_FACTOR_PRESENT;
ic->ic_dm_factor.txpower_factor = value;
}
}
varstart = bootcfg_get_var("dm_aci_factor", tmpbuf);
if (varstart != NULL &&
sscanf(varstart, "=%d", &value) == 1) {
if (value >= DM_ACI_FACTOR_MIN &&
value <= DM_ACI_FACTOR_MAX) {
ic->ic_dm_factor.flags |= DM_FLAG_ACI_FACTOR_PRESENT;
ic->ic_dm_factor.aci_factor = value;
}
}
varstart = bootcfg_get_var("dm_cci_factor", tmpbuf);
if (varstart != NULL &&
sscanf(varstart, "=%d", &value) == 1) {
if (value >= DM_CCI_FACTOR_MIN &&
value <= DM_CCI_FACTOR_MAX) {
ic->ic_dm_factor.flags |= DM_FLAG_CCI_FACTOR_PRESENT;
ic->ic_dm_factor.cci_factor = value;
}
}
varstart = bootcfg_get_var("dm_dfs_factor", tmpbuf);
if (varstart != NULL &&
sscanf(varstart, "=%d", &value) == 1) {
if (value >= DM_DFS_FACTOR_MIN &&
value <= DM_DFS_FACTOR_MAX) {
ic->ic_dm_factor.flags |= DM_FLAG_DFS_FACTOR_PRESENT;
ic->ic_dm_factor.dfs_factor = value;
}
}
varstart = bootcfg_get_var("dm_beacon_factor", tmpbuf);
if (varstart != NULL &&
sscanf(varstart, "=%d", &value) == 1) {
if (value >= DM_BEACON_FACTOR_MIN &&
value <= DM_BEACON_FACTOR_MAX) {
ic->ic_dm_factor.flags |= DM_FLAG_BEACON_FACTOR_PRESENT;
ic->ic_dm_factor.beacon_factor = value;
}
}
}
void qdrv_ic_dump_chan_availability_status(struct ieee80211com *ic)
{
int i;
struct ieee80211_channel * chan = NULL;
const char * str[] = QTN_CHAN_AVAIL_STATUS_TO_STR;
DBGPRINTF_N("Channel Status Status_string\n");
for (i = 1; i < IEEE80211_CHAN_MAX; i++) {
chan = ieee80211_find_channel_by_ieee(ic, i);
if (chan == NULL) {
continue;
}
DBGPRINTF_N("%7d %6d %s\n",
chan->ic_ieee, ic->ic_chan_availability_status[chan->ic_ieee],
str[ic->ic_chan_availability_status[chan->ic_ieee]]);
}
}
static int qdrv_get_chan_availability_status_by_chan_num(struct ieee80211com *ic, struct ieee80211_channel *chan)
{
struct ieee80211_channel *channel = NULL;
if (chan && (channel = ieee80211_find_channel_by_ieee(ic, chan->ic_ieee))) {
return ic->ic_chan_availability_status[channel->ic_ieee];
}
return 0;
}
static void qdrv_set_chan_availability_status_by_chan_num(struct ieee80211com *ic,
struct ieee80211_channel *chan, uint8_t usable)
{
struct ieee80211_channel *channel = NULL;
if (chan && (channel = ieee80211_find_channel_by_ieee(ic, chan->ic_ieee))) {
if (ic->ic_mark_channel_availability_status) {
ic->ic_mark_channel_availability_status(ic, channel, usable);
}
}
return;
}
static void qdrv_mark_channel_availability_status(struct ieee80211com *ic,
struct ieee80211_channel *chan, uint8_t usable)
{
struct ieee80211_channel *low_chan = NULL;
int bw = qdrv_wlan_80211_get_cap_bw(ic);
if (chan == NULL) {
return;
}
if ((chan->ic_flags & IEEE80211_CHAN_RADAR) &&
(usable != IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_RADAR_DETECTED)) {
return;
}
if (!(chan->ic_flags & IEEE80211_CHAN_DFS)) {
return;
}
if (ic->ic_opmode == IEEE80211_M_STA)
bw = MIN(bw, ic->ic_bss_bw);
switch (bw) {
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 && (low_chan + 1) && (low_chan + 2) && (low_chan + 3)) {
ic->ic_chan_availability_status[low_chan->ic_ieee] = usable;
ic->ic_chan_availability_status[(low_chan + 1)->ic_ieee] = usable;
ic->ic_chan_availability_status[(low_chan + 2)->ic_ieee] = usable;
ic->ic_chan_availability_status[(low_chan + 3)->ic_ieee] = usable;
/* If radar found and non-occupancy started, mark all sub-channels as radar found */
if ((usable == IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_RADAR_DETECTED) &&
(chan->ic_flags & IEEE80211_CHAN_RADAR)) {
low_chan->ic_flags |= IEEE80211_CHAN_RADAR;
(low_chan + 1)->ic_flags |= IEEE80211_CHAN_RADAR;
(low_chan + 2)->ic_flags |= IEEE80211_CHAN_RADAR;
(low_chan + 3)->ic_flags |= IEEE80211_CHAN_RADAR;
} else if (usable == IEEE80211_CHANNEL_STATUS_AVAILABLE) {
/*
* Mark primary channel and subchannels as CAC_DONE,
* to prevent CAC being run when set channel is issued
* on one of the sub-channels.
*/
low_chan->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
low_chan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
(low_chan + 1) ->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
(low_chan + 2)->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 2)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
(low_chan + 3)->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 3)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
} else if (usable == IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_CAC_REQUIRED) {
/*
* Non-Occupancy expired; Mark the channels as ready for cac
* Once non-occupancy is period is expired, we should be able to do
* CAC on the channel;
*/
low_chan->ic_flags &= ~IEEE80211_CHAN_RADAR;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_RADAR;
(low_chan + 2)->ic_flags &= ~IEEE80211_CHAN_RADAR;
(low_chan + 3)->ic_flags &= ~IEEE80211_CHAN_RADAR;
low_chan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 2)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 3)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
}
}
break;
case BW_HT40:
if (chan->ic_flags & IEEE80211_CHAN_HT40D) {
low_chan = chan - 1;
} else {
low_chan = chan;
}
if ((low_chan) && (low_chan + 1)) {
ic->ic_chan_availability_status[low_chan->ic_ieee] = usable;
ic->ic_chan_availability_status[(low_chan + 1)->ic_ieee] = usable;
/* If radar found and non-occupancy started, mark all sub-channels as radar found */
if ((usable == IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_RADAR_DETECTED) &&
(chan->ic_flags & IEEE80211_CHAN_RADAR)) {
low_chan->ic_flags |= IEEE80211_CHAN_RADAR;
(low_chan + 1)->ic_flags |= IEEE80211_CHAN_RADAR;
} else if (usable == IEEE80211_CHANNEL_STATUS_AVAILABLE) {
/*
* Mark primary channel and subchannels as CAC_DONE,
* to prevent CAC being run when set channel is issued
* on one of the sub-channels.
*/
low_chan->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
low_chan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
(low_chan + 1) ->ic_flags |= IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_IN_PROGRESS;
} else if (usable == IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_CAC_REQUIRED) {
/*
* Non-Occupancy expired; Mark the channels as ready for cac
* Once non-occupancy is period is expired, we should be able to do
* CAC on the channel
*/
low_chan->ic_flags &= ~IEEE80211_CHAN_RADAR;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_RADAR;
low_chan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
(low_chan + 1)->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
}
}
break;
case BW_HT20:
if (chan) {
ic->ic_chan_availability_status[chan->ic_ieee] = usable;
if (usable == IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_CAC_REQUIRED) {
chan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE;
}
}
break;
default:
printk(KERN_INFO "%s: Invalid bandwidth\n", __func__);
return;
}
if (ic->ic_dump_chan_availability_status) {
ic->ic_dump_chan_availability_status(ic);
}
return;
}
static void qdrv_mark_channel_dfs_cac_status(struct ieee80211com *ic, struct ieee80211_channel *chan, u_int32_t cac_flag, bool set)
{
struct ieee80211_channel *low_chan = NULL;
int bw = qdrv_wlan_80211_get_cap_bw(ic);
if (chan == NULL) {
return;
}
switch (bw) {
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 && (low_chan + 1) && (low_chan + 2) && (low_chan + 3)) {
set ? (low_chan->ic_flags |= cac_flag): (low_chan->ic_flags &= ~cac_flag);
set ? ((low_chan + 1)->ic_flags |= cac_flag): ((low_chan + 1)->ic_flags &= ~cac_flag);
set ? ((low_chan + 2)->ic_flags |= cac_flag): ((low_chan + 2)->ic_flags &= ~cac_flag);
set ? ((low_chan + 3)->ic_flags |= cac_flag): ((low_chan + 3)->ic_flags &= ~cac_flag);
}
break;
case BW_HT40:
if (chan->ic_flags & IEEE80211_CHAN_HT40D) {
low_chan = chan - 1;
} else {
low_chan = chan;
}
if ((low_chan) && (low_chan + 1)) {
set ? (low_chan->ic_flags |= cac_flag): (low_chan->ic_flags &= ~cac_flag);
set ? ((low_chan + 1)->ic_flags |= cac_flag): ((low_chan + 1)->ic_flags &= ~cac_flag);
}
break;
case BW_HT20:
if (chan) {
set ? (chan->ic_flags |= cac_flag): (chan->ic_flags &= ~cac_flag);
}
break;
default:
printk(KERN_INFO "%s: Invalid bandwidth\n", __func__);
return;
}
}
static int qdrv_is_dfs_chans_available_dfs_reentry(struct ieee80211com *ic, struct ieee80211vap *vap)
{
ieee80211_scan_refresh_scan_module_chan_list(ic, vap);
/** Select any DFS channel from {CAC_REQUIRED, AVAILABLE} set */
if (ieee80211_scan_pickchannel(ic, IEEE80211_SCAN_PICK_ANY_DFS)) {
return 1;
}
return -EOPNOTSUPP;
}
/**
* @function : qdrv_dfs_chans_available_for_cac
* @param : ieee80211_channel [ch]: check if this channel is ready for CAC.
* if [ch] is NULL, function returns true if any one channel is ready for CAC
* @brief : returns true if atleast one DFS channel is found for which
* cac not yet done
*/
static bool qdrv_dfs_chans_available_for_cac(struct ieee80211com *ic, struct ieee80211_channel * ch)
{
int i;
int chan = 0;
struct ieee80211_channel * ieee80211_channel = ch;
struct ieee80211_scan_state *ss = ic->ic_scan;
ieee80211_scan_refresh_scan_module_chan_list(ic, TAILQ_FIRST(&ic->ic_vaps));
/* Check channel ch is ready for CAC */
if(ch) {
if((ch->ic_flags & IEEE80211_CHAN_DFS) && ieee80211_is_chan_cac_required(ch)) {
return true;
} else {
return false;
}
}
if (ss) {
for (i = 0; i < ss->ss_last; i++) {
chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
if (!is_channel_valid(chan)) {
continue;
}
ieee80211_channel = ieee80211_find_channel_by_ieee(ic, chan);
if (ieee80211_channel == NULL) {
continue;
}
if ((ieee80211_channel->ic_flags & IEEE80211_CHAN_DFS)
&& ieee80211_is_chan_cac_required(ieee80211_channel)) {
return true;
}
}
}
return false;
}
static int qdrv_get_init_cac_duration(struct ieee80211com *ic)
{
return ic->ic_max_boot_cac_duration;
}
static void qdrv_set_init_cac_duration(struct ieee80211com *ic, int val)
{
ic->ic_max_boot_cac_duration = val;
}
static void qdrv_icac_timer_func(unsigned long arg)
{
struct ieee80211com *ic = (struct ieee80211com *)arg;
if (ic->ic_stop_icac_procedure) {
ic->ic_stop_icac_procedure(ic);
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "%s: Timer expired\n", __func__);
}
static void qdrv_start_icac_procedure(struct ieee80211com *ic)
{
/* Update the boot time CAC timestamp only when ICAC is actually in-progress */
init_timer(&ic->icac_timer);
ic->icac_timer.function = qdrv_icac_timer_func;
ic->icac_timer.data = (unsigned long) ic;
if (ic->ic_get_init_cac_duration) {
if (!ic->ic_boot_cac_end_jiffy && (ic->ic_get_init_cac_duration(ic) > 0)) {
ic->ic_boot_cac_end_jiffy = jiffies + (ic->ic_get_init_cac_duration(ic) * HZ);
ic->icac_timer.expires = ic->ic_boot_cac_end_jiffy;
add_timer(&ic->icac_timer);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "%s: Add init CAC timer\n", __func__);
}
}
}
static void qdrv_stop_icac_procedure(struct ieee80211com *ic)
{
/* set the max_boot_cac_duration to -1 */
if (ic->ic_set_init_cac_duration) {
ic->ic_set_init_cac_duration(ic, -1);
}
/* Stop on-going ICAC timer if any */
del_timer(&ic->icac_timer);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "%s: Init CAC completed\n", __func__);
}
static void qdrv_init_cac_completion_event(struct ieee80211com *ic, struct ieee80211vap *vap)
{
struct ieee80211_channel *bestchan = NULL;
if (ic->ic_get_init_cac_duration(ic) > 0) {
if (ic->ic_stop_icac_procedure) {
ic->ic_stop_icac_procedure(ic);
}
bestchan = ieee80211_find_channel_by_ieee(ic,
ic->ic_ocac.ocac_cfg.ocac_chan_ieee);
if (bestchan && ic->ic_ocac.ocac_cfg.ocac_enable && ic->ic_ocac.ocac_running == 0 &&
ieee80211_is_on_weather_channel(ic, bestchan)) {
bestchan = ieee80211_scan_pickchannel(ic, IEEE80211_SCAN_NO_DFS);
} else if (ic->ic_des_chan_after_init_cac == 0) {
bestchan = ieee80211_scan_pickchannel(ic,
IEEE80211_SCAN_PICK_AVAILABLE_ANY_CHANNEL);
} else {
if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_IBSS ||
vap->iv_opmode == IEEE80211_M_WDS ||
vap->iv_opmode == IEEE80211_M_AHDEMO) &&
!(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER)) {
struct ieee80211_channel *chan = NULL;
/*
* AP operation and we already have a channel;
* bypass the scan and startup immediately.
* But under repeater mode, initiate the AP scan anyway
*/
ic->ic_chan_is_set = 0;
bestchan = ieee80211_find_channel_by_ieee(ic,
ic->ic_des_chan_after_init_cac);
/* to update the fast-switch alternate channel */
chan = ieee80211_scan_pickchannel(ic,
IEEE80211_SCAN_PICK_AVAILABLE_ANY_CHANNEL);
if ((ic->ic_ignore_init_scan_icac) ||
(NULL == bestchan) ||
((bestchan) && (!ic->ic_check_channel(ic, bestchan, 0, 1)))) {
if (chan) {
bestchan = chan;
}
}
ic->ic_chan_is_set = 1;
}
ic->ic_des_chan_after_init_cac = 0;
}
if (bestchan) {
struct ieee80211_scan_state *ss = ic->ic_scan;
ic->ic_des_chan = bestchan;
if (ss && ss->ss_ops) {
struct ap_state *as = ss->ss_priv;
struct ieee80211_scan_entry se;
memset(&se, 0, sizeof(se));
se.se_chan = bestchan;
as->as_selbss = se;
as->as_action = ss->ss_ops->scan_default;
IEEE80211_SCHEDULE_TQUEUE(&as->as_actiontq);
ic->ic_pm_reason = IEEE80211_PM_LEVEL_ICAC_COMPLETE_ACTION;
ieee80211_pm_queue_work_custom(ic, BOARD_PM_WLAN_IDLE_TIMEOUT);
}
} else {
DBGPRINTF_N("%s: failed to select an available channel\n", __func__);
}
}
}
static int qdrv_ap_next_cac(struct ieee80211com *ic, struct ieee80211vap *vap,
unsigned long cac_period,
struct ieee80211_channel **qdrv_radar_cb_cac_chan,
u_int32_t scan_pick_flags)
{
/* ICAC is dependent on the scan module and availability of channels to perform initial CAC */
if ((ic->ic_scan->ss_ops == NULL)
|| (ic->ic_scan->ss_last == 0)
|| (ic->ic_get_init_cac_duration(ic) <= 0))
return -1;
if ((!ieee80211_scan_pickchannel(ic, scan_pick_flags))
|| (!(ic->ic_boot_cac_end_jiffy
&& time_before(jiffies + cac_period, ic->ic_boot_cac_end_jiffy)))) {
if (qdrv_radar_cb_cac_chan) {
*qdrv_radar_cb_cac_chan = NULL;
}
qdrv_init_cac_completion_event(ic, vap);
} else {
if (ic->ic_scan->ss_ops->scan_end) {
/*
* When max_boot_cac timer is too large value, and no channels left for CAC
* stop the ICAC procedure
*/
if (0 == ic->ic_scan->ss_ops->scan_end(ic->ic_scan, vap, NULL, scan_pick_flags)) {
qdrv_init_cac_completion_event(ic, vap);
}
}
}
return 0;
}
static void qdrv_enable_xmit(struct ieee80211com *ic)
{
sys_enable_xmit();
}
static void qdrv_disable_xmit(struct ieee80211com *ic)
{
sys_disable_xmit();
}
static int qdrv_wlan_80211_init(struct ieee80211com *ic, u8 *mac_addr, u8 rf_chipid)
{
int cc_rd = 0;
int nchans;
int i;
/* Set up some dummy channels */
if (rf_chipid == CHIPID_2_4_GHZ) {
nchans = IEEE80211_MAX_2_4_GHZ_CHANNELS;
} else if (rf_chipid == CHIPID_5_GHZ) {
nchans = IEEE80211_MAX_5_GHZ_CHANNELS;
} else {
nchans = IEEE80211_MAX_DUAL_CHANNELS;
}
qdrv_wlan_80211_config_channel(ic, nchans);
ic->ic_ver_sw = QDRV_BLD_VER;
ic->ic_ver_hw = get_hardware_revision();
ic->ic_ver_platform_id = QDRV_CFG_PLATFORM_ID;
ic->ic_ver_timestamp = QDRV_BUILDDATE;
/* Initialize the ieee80211com structure */
ic->ic_config_channel_list = qdrv_wlan_80211_config_channel;
ic->ic_rf_chipid = rf_chipid;
ic->ic_newassoc = qdrv_wlan_80211_newassoc;
ic->ic_disassoc = qdrv_wlan_80211_disassoc;
ic->ic_node_update = qdrv_wlan_80211_node_update;
/* These are called without protection from 802.11 layer */
ic->ic_updateslot = qdrv_wlan_80211_updateslot;
ic->ic_reset = qdrv_wlan_80211_reset;
ic->ic_init = qdrv_wlan_80211_start;
ic->ic_queue_reset = qdrv_wlan_80211_resetmaxqueue;
ic->ic_send_80211 = qdrv_wlan_80211_send;
ic->ic_get_wlanstats = qdrv_wlan_80211_stats;
/* Hook up our code */
ic->ic_join_bss = qdrv_wlan_80211_join_bss;
ic->ic_beacon_update = qdrv_wlan_80211_beacon_update;
ic->ic_beacon_stop = qdrv_wlan_80211_beacon_stop;
ic->ic_set_l2_ext_filter = qdrv_wlan_set_l2_ext_filter;
ic->ic_set_l2_ext_filter_port = qdrv_wlan_set_l2_ext_filter_port;
ic->ic_get_l2_ext_filter_port = qdrv_wlan_get_l2_ext_filter_port;
ic->ic_send_to_l2_ext_filter = qdrv_send_to_l2_ext_filter;
ic->ic_mac_reserved = qdrv_mac_reserved;
ic->ic_setparam = qdrv_wlan_80211_setparam;
ic->ic_getparam = qdrv_wlan_80211_getparam;
ic->ic_register_node = qdrv_wlan_register_node;
ic->ic_unregister_node = qdrv_wlan_unregister_node;
ic->ic_get_phy_stats = qdrv_wlan_80211_get_phy_stats;
ic->ic_get_cca_stats = qdrv_wlan_80211_get_cca_stats;
/* Hook up our Block ack code */
ic->ic_htaddba = qdrv_wlan_80211_process_addba;
ic->ic_htdelba = qdrv_wlan_80211_process_delba;
/* Hook up our Security code */
ic->ic_setkey = qdrv_wlan_80211_setkey;
ic->ic_delkey = qdrv_wlan_80211_delkey;
/* Hook up the MIMO power save mode change */
ic->ic_smps = qdrv_wlan_80211_smps;
/* Function to authorize/deauthorize an STA */
ic->ic_node_auth_state_change = qdrv_wlan_auth_state_change;
/* Station has joined or rejoined a BSS */
ic->ic_new_assoc = qdrv_wlan_new_assoc;
ic->ic_wmm_params_update = qdrv_wlan_update_wmm_params;
ic->ic_vap_pri_wme = 1;
ic->ic_airfair = QTN_AUC_AIRFAIR_DFT;
ic->ic_power_table_update = qdrv_wlan_update_chan_power_table;
ic->ic_power_save = qdrv_wlan_80211_power_save;
ic->ic_remain_on_channel = qdrv_remain_on_channel;
/* Hoop up to set the TDLS parameters */
ic->ic_set_tdls_param = qdrv_wlan_80211_tdls_set_params;
ic->ic_get_tdls_param = qdrv_wlan_80211_tdls_get_params;
ic->ic_peer_rts_mode = IEEE80211_PEER_RTS_DEFAULT;
ic->ic_dyn_wmm = IEEE80211_DYN_WMM_DEFAULT;
ic->ic_tqew_descr_limit = QTN_AUC_TQEW_DESCR_LIMIT_PERCENT_DFT;
/* Should set real opmode here - not just a placeholder */
ic->ic_opmode = IEEE80211_M_STA;
ic->ic_country_code = cc_rd;
ic->ic_spec_country_code = cc_rd;
ic->ic_beaconing_scheme = QTN_BEACONING_SCHEME_0;
ic->ic_set_beaconing_scheme = qdrv_wlan_80211_set_bcn_scheme;
ic->ic_caps = 0;
ic->ic_caps |= IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
| IEEE80211_C_HOSTAP /* hostap mode */
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1 + WPA2 */
| IEEE80211_C_WME /* WMM/WME */
| IEEE80211_C_11N
| IEEE80211_C_TXPMGT /* Capable of Tx Power Management */
| IEEE80211_C_UEQM /* Capable of unequal modulation */
| IEEE80211_C_BGSCAN /* Capable of background scan */
| IEEE80211_C_UAPSD; /* Capable of WMM power save*/
ic->ic_mode_get_phy_stats = MUC_PHY_STATS_ALTERNATE;
ic->ic_rx_agg_timeout = IEEE80211_RX_AGG_TIMEOUT_DEFAULT; /* ms */
ic->ic_legacy_retry_limit = QTN_DEFAULT_LEGACY_RETRY_COUNT;
ic->ic_mu_enable = QTN_GLOBAL_MU_INITIAL_STATE;
ic->ic_vht_mcs_cap = IEEE80211_VHT_MCS_0_9;
/* for WFA testbed */
ic->ic_vht_opmode_notif = IEEE80211_VHT_OPMODE_NOTIF_DEFAULT;
ic->use_non_ht_duplicate_for_mu = 0;
ic->rx_bws_support_for_mu_ndpa = 0;
qdrv_wlan_80211_set_11ac_mode(ic, 1);
if (qdrv_wlan_80211_cfg_ht(ic) != 0)
return -1;
if (qdrv_wlan_80211_cfg_vht(&ic->ic_vhtcap, &ic->ic_vhtop, &ic->ic_vht_nss_cap, 0,
ic->ic_opmode, ic->ic_mu_enable) != 0)
return -1;
if (qdrv_wlan_80211_cfg_vht(&ic->ic_vhtcap_24g, &ic->ic_vhtop_24g, &ic->ic_vht_nss_cap_24g, 1,
ic->ic_opmode, 0) != 0)
return -1;
/* Assign the mac address */
IEEE80211_ADDR_COPY(ic->ic_myaddr, mac_addr);
/* Call MI attach routine. */
ieee80211_ifattach(ic);
ic->ic_node_alloc = qdrv_node_alloc;
ic->ic_qdrv_node_free = qdrv_node_free;
ic->ic_scan_start = qtn_scan_start;
ic->ic_scan_end = qtn_scan_end;
ic->ic_check_channel = qdrv_check_channel;
ic->ic_set_channel = qdrv_set_channel;
ic->ic_get_tsf = hal_get_tsf;
ic->ic_set_channel_deferred = qdrv_set_channel_deferred;
ic->ic_set_start_cca_measurement = qdrv_async_cca_read;
ic->ic_do_measurement = qtn_do_measurement;
ic->ic_finish_measurement = ieee80211_action_finish_measurement;
ic->ic_send_csa_frame = qdrv_wlan_send_csa_frame;
ic->ic_findchannel = findchannel;
ic->ic_cca_token = CCA_TOKEN_INIT_VAL;
ic->ic_set_coverageclass = qtn_set_coverageclass;
ic->ic_mhz2ieee = qtn_mhz2ieee;
ic->ic_vap_create = qtn_vap_create;
ic->ic_vap_delete = qtn_vap_delete;
ic->ic_get_vap_idx = qdrv_get_vap_idx;
ic->ic_radar_detected = qdrv_radar_detected;
ic->ic_select_channel = qdrv_radar_select_newchan;
ic->ic_dfs_action_scan_done = qdrv_dfs_action_scan_done;
ic->ic_dfs_is_eu_region = qdrv_dfs_is_eu_region;
ic->ic_mark_dfs_channels = qdrv_wlan_80211_mark_dfs;
ic->ic_mark_weather_radar_chans = qdrv_wlan_80211_mark_weather_radar;
ic->ic_radar_test_mode_enabled = qdrv_radar_test_mode_enabled;
ic->ic_use_rtscts = qdrv_use_rts_cts;
ic->ic_sta_set_xmit = qdrv_sta_set_xmit;
ic->ic_set_radar = qdrv_set_radar;
ic->ic_enable_sta_dfs = qdrv_sta_dfs_enable;
ic->ic_radar_detections_num = qdrv_radar_detections_num;
ic->ic_complete_cac = qdrv_cac_instant_completed;
ic->ic_sta_assoc_limit = QTN_ASSOC_LIMIT;
for (i = 0; i < IEEE80211_MAX_BSS_GROUP; i++) {
ic->ic_ssid_grp[i].limit = ic->ic_sta_assoc_limit;
ic->ic_ssid_grp[i].reserve = 0;
ic->ic_ssid_grp[i].assocs = 0;
}
ic->ic_emi_power_switch_enable = QTN_EMI_POWER_SWITCH_ENABLE;
#if defined(QBMPS_ENABLE)
ic->ic_bmps_set_frame = qdrv_bmps_set_frame;
ic->ic_bmps_release_frame = qdrv_bmps_release_frame;
#endif
#ifdef QSCS_ENABLED
ic->ic_scs_update_scan_stats = qdrv_scs_update_scan_stats;
ic->ic_sample_channel = qdrv_sample_channel;
ic->ic_sample_channel_cancel = qdrv_sample_channel_cancel;
ic->ic_mark_channel_availability_status = qdrv_mark_channel_availability_status;
ic->ic_set_chan_availability_status_by_chan_num = qdrv_set_chan_availability_status_by_chan_num;
ic->ic_get_chan_availability_status_by_chan_num = qdrv_get_chan_availability_status_by_chan_num;
ic->ic_mark_channel_dfs_cac_status = qdrv_mark_channel_dfs_cac_status;
ic->ic_ap_next_cac = qdrv_ap_next_cac;
ic->ic_dump_chan_availability_status = qdrv_ic_dump_chan_availability_status;
ic->ic_dfs_chans_available_for_cac = qdrv_dfs_chans_available_for_cac;
ic->ic_is_dfs_chans_available_for_dfs_reentry = qdrv_is_dfs_chans_available_dfs_reentry;
ic->ic_get_init_cac_duration = qdrv_get_init_cac_duration;
ic->ic_set_init_cac_duration = qdrv_set_init_cac_duration;
ic->ic_start_icac_procedure = qdrv_start_icac_procedure;
ic->ic_stop_icac_procedure = qdrv_stop_icac_procedure;
ic->ic_chan_compare_equality = qdrv_chan_compare_equality;
ic->ic_enable_xmit = qdrv_enable_xmit;
ic->ic_disable_xmit = qdrv_disable_xmit;
/* defaults for SCS */
ic->ic_scs.scs_enable = 0;
ic->ic_scs.scs_smpl_enable = 0;
ic->ic_scs.scs_stats_on = 0;
ic->ic_scs.scs_debug_enable = 0;
ic->ic_scs.scs_atten_sw_enable = 0;
ic->ic_scs.scs_sample_intv = IEEE80211_SCS_SMPL_INTV_DEFAULT;
ic->ic_scs.scs_sample_type = QTN_OFF_CHAN_FLAG_PASSIVE_NORMAL;
ic->ic_scs.scs_smpl_dwell_time = IEEE80211_SCS_SMPL_DWELL_TIME_DEFAULT;
ic->ic_scs.scs_thrshld_smpl_pktnum = IEEE80211_SCS_THRSHLD_SMPL_PKTNUM_DEFAULT;
ic->ic_scs.scs_thrshld_smpl_airtime = IEEE80211_SCS_THRSHLD_SMPL_AIRTIME_DEFAULT;
ic->ic_scs.scs_thrshld_atten_inc = IEEE80211_SCS_THRSHLD_ATTEN_INC_DFT;
ic->ic_scs.scs_thrshld_dfs_reentry = IEEE80211_SCS_THRSHLD_DFS_REENTRY_DFT;
ic->ic_scs.scs_thrshld_dfs_reentry_intf = IEEE80211_SCS_THRSHLD_DFS_REENTRY_INTF_DFT;
ic->ic_scs.scs_thrshld_aging_nor = IEEE80211_SCS_THRSHLD_AGING_NOR_DFT;
ic->ic_scs.scs_thrshld_aging_dfsreent = IEEE80211_SCS_THRSHLD_AGING_DFSREENT_DFT;
ic->ic_scs.scs_cca_idle_thrshld = IEEE80211_CCA_IDLE_THRSHLD;
ic->ic_scs.scs_cca_intf_lo_thrshld = IEEE80211_CCA_INTFR_LOW_THRSHLD;
ic->ic_scs.scs_cca_intf_hi_thrshld = IEEE80211_CCA_INTFR_HIGH_THRSHLD;
ic->ic_scs.scs_cca_intf_ratio = IEEE80211_CCA_INTFR_RATIO;
ic->ic_scs.scs_cca_intf_dfs_margin = IEEE80211_CCA_INTFR_DFS_MARGIN;
ic->ic_scs.scs_pmbl_err_thrshld = IEEE80211_PMBL_ERR_THRSHLD;
ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_NOXP] =
IEEE80211_CCA_INTF_SMTH_FCTR_NOXP_DFT;
ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_XPED] =
IEEE80211_CCA_INTF_SMTH_FCTR_XPED_DFT;
ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_UP] = IEEE80211_SCS_RSSI_SMTH_FCTR_UP_DFT;
ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_DOWN] = IEEE80211_SCS_RSSI_SMTH_FCTR_DOWN_DFT;
ic->ic_scs.scs_chan_mtrc_mrgn = IEEE80211_SCS_CHAN_MTRC_MRGN_DFT;
ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn = IEEE80211_SCS_LEAVE_DFS_CHAN_MTRC_MRGN_DFT;
ic->ic_scs.scs_atten_adjust = IEEE80211_SCS_ATTEN_ADJUST_DFT;
ic->ic_scs.scs_cca_sample_dur = IEEE80211_CCA_SAMPLE_DUR;
ic->ic_scs.scs_last_smpl_chan = -1;
ic->ic_scs.scs_brcm_rxglitch_thrshlds_scale = IEEE80211_SCS_BRCM_RXGLITCH_THRSHLD_SCALE_DFT;
ic->ic_scs.scs_pmbl_err_smth_fctr = IEEE80211_SCS_PMBL_ERR_SMTH_FCTR_DFT;
ic->ic_scs.scs_pmbl_err_range = IEEE80211_SCS_PMBL_ERR_RANGE_DFT;
ic->ic_scs.scs_pmbl_err_mapped_intf_range = IEEE80211_SCS_PMBL_ERR_MAPPED_INTF_RANGE_DFT;
ic->ic_scs.scs_sp_wf = IEEE80211_SCS_PMBL_SHORT_WF_DFT;
ic->ic_scs.scs_lp_wf = IEEE80211_SCS_PMBL_LONG_WF_DFT;
ic->ic_scs.scs_thrshld_loaded = IEEE80211_SCS_THRSHLD_LOADED_DFT;
ic->ic_scs.scs_pmp_rpt_cca_smth_fctr = IEEE80211_SCS_PMP_RPT_CCA_SMTH_FCTR_DFT;
ic->ic_scs.scs_pmp_rx_time_smth_fctr = IEEE80211_SCS_PMP_RX_TIME_SMTH_FCTR_DFT;
ic->ic_scs.scs_pmp_tx_time_smth_fctr = IEEE80211_SCS_PMP_TX_TIME_SMTH_FCTR_DFT;
ic->ic_scs.scs_pmp_stats_stable_percent = IEEE80211_SCS_PMP_STATS_STABLE_PERCENT_DFT;
ic->ic_scs.scs_pmp_stats_stable_range = IEEE80211_SCS_PMP_STATS_STABLE_RANGE_DFT;
ic->ic_scs.scs_pmp_stats_clear_interval = IEEE80211_SCS_PMP_STATS_CLEAR_INTERVAL_DFT;
ic->ic_scs.scs_as_rx_time_smth_fctr = IEEE80211_SCS_AS_RX_TIME_SMTH_FCTR_DFT;
ic->ic_scs.scs_as_tx_time_smth_fctr = IEEE80211_SCS_AS_TX_TIME_SMTH_FCTR_DFT;
ic->ic_scs.scs_cca_idle_smth_fctr = IEEE80211_SCS_CCA_IDLE_SMTH_FCTR_DFT;
spin_lock_init(&ic->ic_scs.scs_tdls_lock);
ic->ic_scs.scs_burst_enable = IEEE80211_SCS_BURST_ENABLE_DEFAULT;
ic->ic_scs.scs_burst_window = IEEE80211_SCS_BURST_WINDOW_DEFAULT * 60;
ic->ic_scs.scs_burst_thresh = IEEE80211_SCS_BURST_THRESH_DEFAULT;
ic->ic_scs.scs_burst_pause_time = IEEE80211_SCS_BURST_PAUSE_DEFAULT * 60;
ic->ic_scs.scs_burst_force_switch = IEEE80211_SCS_BURST_SWITCH_DEFAULT;
ic->ic_scs.scs_burst_is_paused = 0;
#endif /* QSCS_ENABLED */
ic->ic_ocac.ocac_cfg.ocac_enable = 0;
ic->ic_ocac.ocac_cfg.ocac_chan_ieee = 0;
ic->ic_ocac.ocac_cfg.ocac_debug_level = 0;
ic->ic_ocac.ocac_cfg.ocac_report_only = 0;
strncpy(ic->ic_ocac.ocac_cfg.ocac_region, "NA", sizeof(ic->ic_ocac.ocac_cfg.ocac_region));
ic->ic_ocac.ocac_cfg.ocac_timer_expire_init = IEEE80211_OCAC_TIMER_EXPIRE_INIT_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.timer_interval = IEEE80211_OCAC_TIMER_INTERVAL_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.secure_dwell_ms = IEEE80211_OCAC_SECURE_DWELL_TIME_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.dwell_time_ms = IEEE80211_OCAC_DWELL_TIME_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.duration_secs = IEEE80211_OCAC_DURATION_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.cac_time_secs = IEEE80211_OCAC_CAC_TIME_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.wea_dwell_time_ms = IEEE80211_OCAC_WEA_DWELL_TIME_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.wea_duration_secs = IEEE80211_OCAC_WEA_DURATION_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.wea_cac_time_secs = IEEE80211_OCAC_WEA_CAC_TIME_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat = IEEE80211_OCAC_THRESHOLD_FAT_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.thresh_traffic = IEEE80211_OCAC_THRESHOLD_TRAFFIC_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.thresh_cca_intf = IEEE80211_OCAC_THRESHOLD_CCA_INTF_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat_dec = IEEE80211_OCAC_THRESHOLD_FAT_DEC_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.traffic_ctrl = IEEE80211_OCAC_TRAFFIC_CTRL_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.offset_txhalt = IEEE80211_OCAC_OFFSET_TXHALT_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.offset_offchan = IEEE80211_OCAC_OFFSET_OFFCHAN_DEFAULT;
ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval = IEEE80211_OCAC_BEACON_INTERVAL_DEFAULT;
ic->ic_ocac.ocac_tsflog.log_index = 0;
ic->ic_set_ocac = qdrv_set_ocac;
ic->ic_ocac_release_frame = qdrv_ocac_release_frame;
ic->ic_rxtx_phy_rate = qdrv_muc_stats_rxtx_phy_rate;
ic->ic_rssi = qdrv_muc_stats_rssi;
ic->ic_smoothed_rssi = qdrv_muc_stats_smoothed_rssi;
ic->ic_snr = qdrv_muc_stats_snr;
ic->ic_hw_noise = qdrv_muc_stats_hw_noise;
ic->ic_max_queue = qdrv_muc_stats_max_queue;
ic->ic_mcs_to_phyrate = qdrv_muc_stats_mcs_to_phyrate;
ic->ic_tx_failed = qdrv_muc_stats_tx_failed;
ic->ic_chan_switch_record = qdrv_channel_switch_record;
ic->ic_chan_switch_reason_record = qdrv_channel_switch_reason_record;
ic->ic_dfs_chan_switch_notify = dfs_reentry_chan_switch_notify;
ic->ic_set_11g_erp = qdrv_wlan_set_11g_erp;
/* shared CSA framework */
init_completion(&ic->csa_completion);
INIT_WORK(&ic->csa_work, ieee80211_csa_finish);
ic->csa_work_queue = create_workqueue("csa_work_queue");
ic->finish_csa = NULL;
ic->ic_20_40_coex_enable = 1;
ic->ic_obss_scan_enable = 1;
ic->ic_obss_scan_count = 0;
init_timer(&ic->ic_obss_timer);
ic->ic_obss_ie.obss_passive_dwell = IEEE80211_OBSS_PASSIVE_DWELL_DEFAULT;
ic->ic_obss_ie.obss_active_dwell = IEEE80211_OBSS_ACTIVE_DWELL_DEFAULT;
ic->ic_obss_ie.obss_trigger_interval = IEEE80211_OBSS_TRIGGER_INTERVAL_DEFAULT;
ic->ic_obss_ie.obss_passive_total = IEEE80211_OBSS_PASSIVE_TOTAL_DEFAULT;
ic->ic_obss_ie.obss_active_total = IEEE80211_OBSS_ACTIVE_TOTAL_DEFAULT;
ic->ic_obss_ie.obss_channel_width_delay = IEEE80211_OBSS_CHANNEL_WIDTH_DELAY_DEFAULT;
ic->ic_obss_ie.obss_activity_threshold = IEEE80211_OBSS_ACTIVITY_THRESHOLD_DEFAULT;
ic->ic_coex_stats_update = qdrv_wlan_coex_stats_update;
ic->ic_neighbor_count = -1;
ic->ic_neighbor_cnt_sparse = IEEE80211_NEIGHBORHOOD_TYPE_SPARSE_DFT_THRSHLD;
ic->ic_neighbor_cnt_dense = IEEE80211_NEIGHBORHOOD_TYPE_DENSE_DFT_THRSHLD;
#ifdef CONFIG_QVSP
ic->ic_vsp_strm_state_set = qdrv_wlan_vsp_strm_state_set;
ic->ic_vsp_change_stamode = qdrv_wlan_vsp_change_stamode;
ic->ic_vsp_configure = qdrv_wlan_vsp_configure;
ic->ic_vsp_set = qdrv_wlan_vsp_set;
ic->ic_vsp_get = qdrv_wlan_vsp_get;
ic->ic_vsp_cb_strm_ctrl = qdrv_wlan_vsp_cb_strm_ctrl;
ic->ic_vsp_cb_cfg = qdrv_wlan_vsp_cb_cfg;
ic->ic_vsp_reset = qdrv_wlan_vsp_reset;
ic->ic_vsp_cb_strm_ext_throttler = qdrv_wlan_vsp_cb_strm_ext_throttler;
#endif
#ifdef QTN_BG_SCAN
ic->ic_bgscan_start = qdrv_bgscan_start;
ic->ic_bgscan_channel = qdrv_bgscan_channel;
#endif /* QTN_BG_SCAN */
/* we don't have short range issue with Topaz */
ic->ic_pwr_adjust_scancnt = 0;
/* initiate data struct that record channel switch */
memset(&ic->ic_csw_record, 0, sizeof(ic->ic_csw_record));
memset(&ic->ic_chan_occupy_record, 0, sizeof(ic->ic_chan_occupy_record));
ic->ic_send_notify_chan_width_action = ieee80211_send_notify_chan_width_action;
ic->ic_send_vht_grp_id_act = ieee80211_send_vht_grp_id_mgmt_action;
qdrv_wlan_pm_state_init(ic);
ic->ic_get_local_txpow = qdrv_get_local_tx_power;
ic->ic_get_local_link_margin = qdrv_get_local_link_margin;
ic->ic_get_shared_vap_stats = qdrv_wlan_get_shared_vap_stats;
ic->ic_reset_shared_vap_stats = qdrv_wlan_reset_shared_vap_stats;
ic->ic_get_shared_node_stats = qdrv_wlan_get_shared_node_stats;
ic->ic_reset_shared_node_stats = qdrv_wlan_reset_shared_node_stats;
ic->ic_get_dscp2ac_map = qdrv_wlan_get_dscp2ac_map;
ic->ic_set_dscp2ac_map = qdrv_wlan_set_dscp2ac_map;
ic->ic_get_dscp2tid_map = qdrv_sch_get_dscp2tid_map;
ic->ic_set_dscp2tid_map = qdrv_sch_set_dscp2tid_map;
ic->ic_pco.pco_set = 0;
ic->ic_pco.pco_pwr_constraint = 0;
ic->ic_pco.pco_rssi_threshold = 0;
ic->ic_pco.pco_sec_offset = 0;
ic->ic_pco.pco_pwr_constraint_save = PWR_CONSTRAINT_SAVE_INIT;
ic->ic_su_txbf_pkt_cnt = QTN_SU_TXBF_TX_CNT_DEF_THRSHLD;
ic->ic_mu_txbf_pkt_cnt = QTN_MU_TXBF_TX_CNT_DEF_THRSHLD;
ic->ic_get_cca_adjusting_status = ieee80211_get_cca_adjusting_status;
ic->ic_flags_qtn |= QTN_NODE_11N_TXAMSDU_OFF;
ic->ic_flags_ext |= IEEE80211_FEXT_UAPSD;
ic->ic_extender_rssi_continue = 0;
ic->ic_dfs_csa_cnt = 1;
qdrv_wlan_init_dm_factors(ic);
/* tx airtime callback init */
ic->ic_tx_airtime = qdrv_muc_stats_tx_airtime;
ic->ic_tx_accum_airtime = qdrv_muc_stats_tx_accum_airtime;
ic->ic_tx_airtime_control = qdrv_tx_airtime_control;
ic->ic_rx_airtime = qdrv_muc_stats_rx_airtime;
ic->ic_rx_accum_airtime = qdrv_muc_stats_rx_accum_airtime;
/* MU group state update */
ic->ic_mu_group_update = qdrv_mu_grp_update;
ic->sta_dfs_info.sta_dfs_strict_mode = 0;
ic->sta_dfs_info.sta_dfs_radar_detected_timer = 0;
ic->sta_dfs_info.sta_dfs_radar_detected_channel = 0;
ic->sta_dfs_info.sta_dfs_strict_msr_cac = 0;
ic->sta_dfs_info.allow_measurement_report = 0;
ic->sta_dfs_info.sta_dfs_tx_chan_close_time = STA_DFS_STRICT_TX_CHAN_CLOSE_TIME_DEFAULT;
#ifdef CONFIG_QHOP
ic->rbs_mbs_dfs_info.rbs_dfs_tx_chan_close_time = RBS_DFS_TX_CHAN_CLOSE_TIME_DEFAULT;
#endif
ic->auto_cca_enable = 0x1;
ic->cca_fix_disable = 0;
ic->ic_opmode_bw_switch_en = 0;
spin_lock_init(&ic->ic_ocac.ocac_lock);
memset(&ic->ic_ocac.ocac_rx_state, 0, sizeof(ic->ic_ocac.ocac_rx_state));
ic->ic_update_ocac_state_ie = qdrv_update_ocac_state_ie;
return 0;
}
void qdrv_wlan_get_dscp2ac_map(const uint8_t vapid, uint8_t *dscp2ac)
{
qdrv_sch_get_dscp2ac_map(vapid, dscp2ac);
return;
}
void qdrv_wlan_set_dscp2ac_map(const uint8_t vapid, uint8_t *ip_dscp, uint8_t listlen, uint8_t ac)
{
qdrv_sch_set_dscp2ac_map(vapid, ip_dscp, listlen, ac);
return;
}
static int qdrv_wlan_80211_exit(struct ieee80211com *ic)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ic->finish_csa = NULL;
del_timer_sync(&ic->ic_obss_timer);
pm_qos_remove_notifier(PM_QOS_POWER_SAVE, &qw->pm_notifier);
del_timer(&ic->ic_pm_period_change);
ieee80211_ifdetach(ic);
#ifdef QTN_BG_SCAN
qdrv_bgscan_release_frame(ic, IEEE80211_SCAN_FRAME_ALL, 1);
#endif /* QTN_BG_SCAN */
qdrv_scs_release_frame(ic, 1);
qdrv_ocac_release_frame(ic, 1);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
static void qdrv_show_wlan_stats(struct seq_file *s, void *data, u32 num)
{
struct qdrv_mac *mac = (struct qdrv_mac *) data;
struct qdrv_wlan *qw = (struct qdrv_wlan *) mac->data;
struct qtn_skb_recycle_list *recycle_list = qtn_get_shared_recycle_list();
int i;
DBGPRINTF(DBG_LL_ERR, QDRV_LF_TRACE, "-->Enter %d\n", num);
seq_printf(s, "TX Statistics\n");
seq_printf(s, " tx_enqueue_mgmt : %d\n", qw->tx_stats.tx_enqueue_mgmt);
seq_printf(s, " tx_enqueue_80211_data : %d\n", qw->tx_stats.tx_enqueue_80211_data);
seq_printf(s, " tx_enqueue_data : %d\n", qw->tx_stats.tx_enqueue_data);
seq_printf(s, " tx_muc_enqueue : %d\n", qw->tx_stats.tx_muc_enqueue);
seq_printf(s, " tx_muc_enqueue_mbox : %d\n", qw->tx_stats.tx_muc_enqueue_mbox);
seq_printf(s, " tx_null_data : %d\n", qw->tx_stats.tx_null_data);
seq_printf(s, " tx_done_success : %d\n", qw->tx_stats.tx_done_success);
seq_printf(s, " tx_done_muc_ready_err : %d\n", qw->tx_stats.tx_done_muc_ready_err);
seq_printf(s, " tx_done_enable_queues : %d\n", qw->tx_stats.tx_done_enable_queues);
seq_printf(s, " tx_queue_stop : %d\n", qw->tx_stats.tx_queue_stop);
seq_printf(s, " tx_requeue : %d\n", qw->tx_stats.tx_requeue);
seq_printf(s, " tx_requeue_err : %d\n", qw->tx_stats.tx_requeue_err);
seq_printf(s, " tx_hardstart : %d\n", qw->tx_stats.tx_hardstart);
seq_printf(s, " tx_complete : %d\n", qw->tx_stats.tx_complete);
seq_printf(s, " tx_min_cl_cnt : %d\n", qw->tx_stats.tx_min_cl_cnt);
seq_printf(s, " txdesc_data : %d\n", qw->tx_if.txdesc_cnt[QDRV_TXDESC_DATA]);
seq_printf(s, " txdesc_mgmt : %d\n", qw->tx_if.txdesc_cnt[QDRV_TXDESC_MGMT]);
seq_printf(s, " tx_dropped_mac_dead : %d\n", qw->tx_stats.tx_dropped_mac_dead);
seq_printf(s, " tx_igmp : %d\n", qw->tx_stats.tx_igmp);
seq_printf(s, " tx_unknown : %d\n", qw->tx_stats.tx_unknown);
seq_printf(s, " tx_arp_req : %d\n", qw->tx_stats.tx_arp_req);
seq_printf(s, " tx_copy4_mc : %d\n", qw->tx_stats.tx_copy4_mc);
seq_printf(s, " tx_copy4_igmp : %d\n", qw->tx_stats.tx_copy4_igmp);
seq_printf(s, " tx_copy4_unknown : %d\n", qw->tx_stats.tx_copy4_unknown);
seq_printf(s, " tx_copy4 : %d\n", qw->tx_stats.tx_copy4);
seq_printf(s, " tx_copy_fail : %d\n", qw->tx_stats.tx_copy_fail);
seq_printf(s, " tx_copy4_busy : %d\n", qw->tx_stats.tx_copy4_busy);
seq_printf(s, " tx_copy3_mc : %d\n", qw->tx_stats.tx_copy3_mc);
seq_printf(s, " tx_copy3_igmp : %d\n", qw->tx_stats.tx_copy3_igmp);
seq_printf(s, " tx_copy_uc : %d\n", qw->tx_stats.tx_copy_uc);
seq_printf(s, " tx_copy_mc : %d\n", qw->tx_stats.tx_copy_mc);
seq_printf(s, " tx_copy_mc_to_uc : %d\n", qw->tx_stats.tx_copy_mc_to_uc);
seq_printf(s, " tx_copy_ssdp : %d\n", qw->tx_stats.tx_copy_ssdp);
seq_printf(s, " tx_copy3 : %d\n", qw->tx_stats.tx_copy3);
seq_printf(s, " tx_drop_auth : %d\n", qw->tx_stats.tx_drop_auth);
seq_printf(s, " tx_drop_aid : %d\n", qw->tx_stats.tx_drop_aid);
seq_printf(s, " tx_drop_nodesc : %d\n", qw->tx_stats.tx_drop_nodesc);
seq_printf(s, " tx_drop_wds : %d\n", qw->tx_stats.tx_drop_wds);
seq_printf(s, " tx_drop_3addr : %d\n", qw->tx_stats.tx_drop_3addr);
seq_printf(s, " tx_drop_vsp : %d\n", qw->tx_stats.tx_drop_vsp);
seq_printf(s, " tx_dropped_config : %d\n", qw->tx_stats.tx_dropped_config);
seq_printf(s, " tx_drop_total : %d\n", qw->tx_stats.tx_drop_total);
seq_printf(s, " tx_channel : %d\n", qw->tx_stats.tx_channel);
seq_printf(s, " tx_l2_ext_filter : %d\n", qw->tx_stats.tx_l2_ext_filter);
seq_printf(s, " tx_drop_l2_ext_filter : %d\n", qw->tx_stats.tx_drop_l2_ext_filter);
seq_printf(s, " tx_prot_arp : %u\n", qw->tx_stats.prot_arp);
seq_printf(s, " tx_prot_pae : %u\n", qw->tx_stats.prot_pae);
seq_printf(s, " tx_prot_ip_udp : %u\n", qw->tx_stats.prot_ip_udp);
seq_printf(s, " tx_prot_ip_tcp : %u\n", qw->tx_stats.prot_ip_tcp);
seq_printf(s, " tx_prot_ip_icmp : %u\n", qw->tx_stats.prot_ip_icmp);
seq_printf(s, " tx_prot_ip_igmp : %u\n", qw->tx_stats.prot_ip_igmp);
seq_printf(s, " tx_prot_ip_other : %u\n", qw->tx_stats.prot_ip_other);
seq_printf(s, " tx_prot_ipv6 : %u\n", qw->tx_stats.prot_ipv6);
seq_printf(s, " tx_prot_other : %u\n", qw->tx_stats.prot_other);
seq_printf(s, "RX Statistics\n");
seq_printf(s, " rx_irq : %d\n", qw->rx_stats.rx_irq);
seq_printf(s, " rx_irq_schedule : %d\n", qw->rx_stats.rx_irq_schedule);
seq_printf(s, " rx_beacon : %d\n", qw->rx_stats.rx_beacon);
seq_printf(s, " rx_non_beacon : %d\n", qw->rx_stats.rx_non_beacon);
seq_printf(s, " rx_input_all : %d\n", qw->rx_stats.rx_input_all);
seq_printf(s, " rx_input_node : %d\n", qw->rx_stats.rx_input_node);
seq_printf(s, " rx_data_snap : %d\n", qw->rx_stats.rx_data_snap);
seq_printf(s, " rx_data_tods : %d\n", qw->rx_stats.rx_data_tods);
seq_printf(s, " rx_data_nods : %d\n", qw->rx_stats.rx_data_nods);
seq_printf(s, " rx_data_fromds : %d\n", qw->rx_stats.rx_data_fromds);
seq_printf(s, " rx_data_dstods : %d\n", qw->rx_stats.rx_data_dstods);
seq_printf(s, " rx_data_no_node : %d\n", qw->rx_stats.rx_data_no_node);
seq_printf(s, " rx_data_too_short : %d\n", qw->rx_stats.rx_data_too_short);
seq_printf(s, " rx_poll : %d\n", qw->rx_stats.rx_poll);
seq_printf(s, " rx_poll_pending : %d\n", qw->rx_stats.rx_poll_pending);
seq_printf(s, " rx_poll_empty : %d\n", qw->rx_stats.rx_poll_empty);
seq_printf(s, " rx_poll_retrieving : %d\n", qw->rx_stats.rx_poll_retrieving);
seq_printf(s, " rx_poll_buffer_err : %d\n", qw->rx_stats.rx_poll_buffer_err);
seq_printf(s, " rx_poll_skballoc_err : %d\n", qw->rx_stats.rx_poll_skballoc_err);
seq_printf(s, " rx_poll_stopped : %d\n", qw->rx_stats.rx_poll_stopped);
seq_printf(s, " rx_df_numelems : %d\n", qw->rx_stats.rx_df_numelems);
seq_printf(s, " rx_amsdu : %d\n", qw->rx_stats.rx_amsdu);
seq_printf(s, " rx_packets : %d\n", qw->rx_stats.rx_packets);
seq_printf(s, " rx_bytes : %d\n", qw->rx_stats.rx_bytes);
seq_printf(s, " rx_poll_next : %d\n", qw->rx_stats.rx_poll_next);
seq_printf(s, " rx_poll_complete : %d\n", qw->rx_stats.rx_poll_complete);
seq_printf(s, " rx_poll_continue : %d\n", qw->rx_stats.rx_poll_continue);
seq_printf(s, " rx_poll_vap_err : %d\n", qw->rx_stats.rx_poll_vap_err);
seq_printf(s, " rx_frag : %d\n", qw->rx_stats.rx_frag);
seq_printf(s, " rx_lncb_4 : %d\n", qw->rx_stats.rx_lncb_4);
seq_printf(s, " rx_blacklist : %d\n", qw->rx_stats.rx_blacklist);
seq_printf(s, " rx_igmp : %d\n", qw->rx_stats.rx_igmp);
seq_printf(s, " rx_igmp_4 : %d\n", qw->rx_stats.rx_igmp_4);
seq_printf(s, " rx_igmp_3_drop : %d\n", qw->rx_stats.rx_igmp_3_drop);
seq_printf(s, " rx_mc_3_drop : %d\n", qw->rx_stats.rx_mc_3_drop);
seq_printf(s, " rx_prot_arp : %u\n", qw->rx_stats.prot_arp);
seq_printf(s, " rx_prot_pae : %u\n", qw->rx_stats.prot_pae);
seq_printf(s, " rx_prot_ip_udp : %u\n", qw->rx_stats.prot_ip_udp);
seq_printf(s, " rx_prot_ip_tcp : %u\n", qw->rx_stats.prot_ip_tcp);
seq_printf(s, " rx_prot_ip_icmp : %u\n", qw->rx_stats.prot_ip_icmp);
seq_printf(s, " rx_prot_ip_igmp : %u\n", qw->rx_stats.prot_ip_igmp);
seq_printf(s, " rx_prot_ip_other : %u\n", qw->rx_stats.prot_ip_other);
seq_printf(s, " rx_prot_ipv6 : %u\n", qw->rx_stats.prot_ipv6);
seq_printf(s, " rx_prot_other : %u\n", qw->rx_stats.prot_other);
seq_printf(s, " rx_rate_train_invalid : %u\n", qw->rx_stats.rx_rate_train_invalid);
seq_printf(s, " rx_mac_reserved : %u\n", qw->rx_stats.rx_mac_reserved);
seq_printf(s, " rx_coex_bw_action : %u\n", qw->rx_stats.rx_coex_bw_action);
seq_printf(s, " rx_coex_bw_assoc : %u\n", qw->rx_stats.rx_coex_bw_assoc);
seq_printf(s, " rx_coex_bw_scan : %u\n", qw->rx_stats.rx_coex_bw_scan);
seq_printf(s, "Recycling Statistics\n");
seq_printf(s, " qdrv_free_pass : %d\n", recycle_list->stats_qdrv.free_recycle_pass);
seq_printf(s, " qdrv_free_fail : %d\n", recycle_list->stats_qdrv.free_recycle_fail);
seq_printf(s, " qdrv_free_fail_undersize : %d\n", recycle_list->stats_qdrv.free_recycle_fail_undersize);
seq_printf(s, " qdrv_alloc_recycle : %d\n", recycle_list->stats_qdrv.alloc_recycle);
seq_printf(s, " qdrv_alloc_kmalloc : %d\n", recycle_list->stats_qdrv.alloc_kmalloc);
seq_printf(s, " eth_free_pass : %d\n", recycle_list->stats_eth.free_recycle_pass);
seq_printf(s, " eth_free_fail : %d\n", recycle_list->stats_eth.free_recycle_fail);
seq_printf(s, " eth_free_fail_undersize : %d\n", recycle_list->stats_eth.free_recycle_fail_undersize);
seq_printf(s, " eth_alloc_recycle : %d\n", recycle_list->stats_eth.alloc_recycle);
seq_printf(s, " eth_alloc_kmalloc : %d\n", recycle_list->stats_eth.alloc_kmalloc);
#if defined(CONFIG_RUBY_PCIE_HOST) || defined(CONFIG_RUBY_PCIE_TARGET)
seq_printf(s, " pcie_free_pass : %d\n", recycle_list->stats_pcie.free_recycle_pass);
seq_printf(s, " pcie_free_fail : %d\n", recycle_list->stats_pcie.free_recycle_fail);
seq_printf(s, " pcie_free_fail_undersize : %d\n", recycle_list->stats_pcie.free_recycle_fail_undersize);
seq_printf(s, " pcie_alloc_recycle : %d\n", recycle_list->stats_pcie.alloc_recycle);
seq_printf(s, " pcie_alloc_kmalloc : %d\n", recycle_list->stats_pcie.alloc_kmalloc);
#endif
seq_printf(s, " kfree_free_pass : %d\n", recycle_list->stats_kfree.free_recycle_pass);
seq_printf(s, " kfree_free_fail : %d\n", recycle_list->stats_kfree.free_recycle_fail);
seq_printf(s, " kfree_free_fail_undersize : %d\n", recycle_list->stats_kfree.free_recycle_fail_undersize);
seq_printf(s, "BF Statistics\n");
for (i = 0; i < QTN_STATS_NUM_BF_SLOTS; i++) {
seq_printf(s, " slot %u success : %d\n", i, qw->rx_stats.rx_bf_success[i]);
}
for (i = 0; i < QTN_STATS_NUM_BF_SLOTS; i++) {
seq_printf(s, " slot %u rejected : %d\n", i, qw->rx_stats.rx_bf_rejected[i]);
}
seq_printf(s, "QCAT state: %d\n", qw->tx_stats.qcat_state);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return;
}
int qdrv_wlan_stats(void *data)
{
qdrv_control_set_show(qdrv_show_wlan_stats, data, 1, 1);
return 0;
}
static void
qdrv_wlan_show_assoc_queue_info(struct seq_file *s, void *data, u32 num)
{
struct qdrv_wlan *qw = data;
struct ieee80211com *ic = &qw->ic;
struct qdrv_sch_shared_data *sd = qw->tx_sch_shared_data;
struct qdrv_mac *mac = qw->mac;
struct qdrv_vap *qv;
struct net_device *dev;
struct Qdisc *sch;
uint32_t i;
seq_printf(s, "shared data: users=%d tokens=%u/%u res=%u rdt=%u muc_thresh=%u/%u\n",
sd->users,
sd->total_tokens,
sd->available_tokens,
sd->reserved_tokens_per_user,
sd->random_drop_threshold,
qw->tx_if.muc_thresh_high,
qw->tx_if.muc_thresh_low);
for (i = 0; i <= mac->vnet_last; ++i) {
dev = mac->vnet[i];
if (dev && (dev->flags & IFF_UP)) {
sch = qdrv_tx_sch_vap_get_qdisc(dev);
qv = netdev_priv(dev);
if (sch) {
seq_printf(s, "%s qdisc=%p queued=%u muc=%u\n",
dev->name, sch, sch->q.qlen, qv->muc_queued);
}
}
}
ic->ic_iterate_nodes(&ic->ic_sta, qdrv_wlan_tx_sch_node_info, (void *)s, 1);
}
/*
* If BIT(24) is set, it stands for all nodes.
* If not, 8 LSBs stands for the node index.
* BIT(8)-BIT(15) stands for the control masks.
*/
void qdrv_tx_airtime_control(struct ieee80211vap *vap, uint32_t value)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct qdrv_wlan *qw = qv->parent;
qdrv_hostlink_tx_airtime_control(qw, value);
}
void qdrv_mu_grp_update(struct ieee80211com *ic, struct qtn_mu_group_update_args *args)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qdrv_hostlink_mu_group_update(qw, args);
}
static void
qdrv_wlan_show_assoc_info( struct seq_file *s, void *data, u32 num )
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) data;
struct ieee80211com *ic = &qw->ic;
ic->ic_iterate_nodes(&ic->ic_sta, get_node_info, (void *)s, 1);
}
#ifdef CONFIG_NAC_MONITOR
static void
qdrv_wlan_show_nac_info( struct seq_file *s, void *data, u32 num )
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
struct nac_mon_info *info = sp->nac_mon_info;
struct nac_stats_entry *entry = &info->nac_stats[0];
int i;
seq_printf(s, " Mac Address Rssi(dB) Timestamp Channel Packet Type\n");
for (i = 0; i < MAX_NAC_STA; i++, entry++) {
if(entry->nac_valid) {
seq_printf(s, "%s %9d %10llu %8d %-10s\n",
ether_sprintf(&entry->nac_txmac[0]),
(int8_t)entry->nac_avg_rssi,
entry->nac_timestamp,
entry->nac_channel,
(entry->nac_packet_type == 1)?"Control":
((entry->nac_packet_type == 2)?"Data":"Management"));
}
}
}
int
qdrv_wlan_get_nac_info(void *data)
{
qdrv_control_set_show(qdrv_wlan_show_nac_info, data, 1, 1);
return 0;
}
#endif
int
qdrv_wlan_get_assoc_queue_info(void *data)
{
qdrv_control_set_show(qdrv_wlan_show_assoc_queue_info, data, 1, 1);
return 0;
}
int
qdrv_wlan_get_assoc_info(void *data)
{
qdrv_control_set_show(qdrv_wlan_show_assoc_info, data, 1, 1);
return 0;
}
int qdrv_wlan_start_vap(struct qdrv_wlan *qw, const char *name,
uint8_t *mac_addr, int devid, int opmode, int flags)
{
int ret;
struct ieee80211com *ic = &qw->ic;
switch (opmode) {
case IEEE80211_M_HOSTAP:
if (!(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER) &&
!ieee80211_swfeat_is_supported(SWFEAT_ID_MODE_AP, 1)) {
return -1;
}
break;
case IEEE80211_M_WDS:
if (!ieee80211_swfeat_is_supported(SWFEAT_ID_MODE_AP, 1))
return -1;
break;
case IEEE80211_M_STA:
if (!ieee80211_swfeat_is_supported(SWFEAT_ID_MODE_STA, 1))
return -1;
break;
default:
printk("mode %u is not supported on this device\n", opmode);
return -1;
}
ret = qdrv_hostlink_msg_create_vap(qw, name, mac_addr, devid, opmode, flags);
if (ret < 0) {
DBGPRINTF_E("Failed to send create VAP message\n");
}
return ret;
}
int qdrv_wlan_stop_vap(struct qdrv_mac *mac, struct net_device *vdev)
{
struct qdrv_wlan *qw = qdrv_mac_get_wlan(mac);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (qdrv_hostlink_msg_delete_vap(qw, vdev) < 0) {
DBGPRINTF_E("Failed to delete VAP on MuC\n");
return -1;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
#define DEFAULT_NUM_TEMP_ZONE 15
#define MAX_NUM_TEMP_ZONE 50
#define MAX_SIZE_TEMP_PROFILE_BUFF 200
#define QDRV_TEMP_CAL_PERIOD (5 * HZ)
#define TPROFILE_IN_TEMP "/tmp/tprofile.txt"
#define TPROFILE_IN_PROC "/proc/bootcfg/tprofile.txt"
#define PDETECTOR_IN_PROC "/proc/bootcfg/pdetector.cal"
struct _temp_info *p_muc_temp_index;
static int tpf[MAX_NUM_TEMP_ZONE] = { 0xFFFFFFF };
static inline char *qdrv_txpow_cal_strip_all_white_space(char *str)
{
char *p;
char *s_ptr = str;
p = str;
do {
if (!isspace(*p = *str)) {
p++;
}
} while (*str++);
return s_ptr;
}
static int qdrv_txpow_cal_tzone_get(char *temperature_profile)
{
char *from;
char *value;
int *d_ptr;
int num_of_temp_zone = 0;
from = qdrv_txpow_cal_strip_all_white_space(temperature_profile);
d_ptr = &tpf[0];
while (from) {
value = strsep(&from, ",");
*d_ptr = simple_strtoul(value, NULL, 0);
d_ptr++;
num_of_temp_zone++;
}
return num_of_temp_zone;
}
static int qdrv_txpow_cal_convert_temp_index(struct qdrv_wlan *qw, int temp)
{
int temp_index = 0;
int i = 0;
int findit = 0;
for (i = 0; i < qw->tx_power_cal_data.temp_info.num_zone; i++) {
if (temp >= tpf[i] && temp < tpf[i + 1]) {
temp_index = i + 1;
findit = 1;
break;
}
}
if (findit != 1) {
if (temp < tpf[0])
temp_index = 0;
else if (temp > tpf[qw->tx_power_cal_data.temp_info.num_zone - 1])
temp_index = qw->tx_power_cal_data.temp_info.num_zone;
}
return temp_index;
}
static void qdrv_init_tsensor(struct qdrv_wlan *qw)
{
struct i2c_board_info se95_info = {
I2C_BOARD_INFO("se95", SE95_DEVICE_ADDR),
};
int temp;
struct i2c_adapter *adapter;
adapter = i2c_get_adapter(RUBY_I2C_ADAPTER_NUM);
if (!adapter) {
qw->se95_temp_sensor = NULL;
printk("QDRV: I2C dapter not found\n");
return;
}
qw->se95_temp_sensor = i2c_new_device(adapter, &se95_info);
if (!qw->se95_temp_sensor) {
i2c_put_adapter(adapter);
DBGPRINTF_E("Failed to instantiate temperature sensor device\n");
return;
}
/*
* i2c_new_device will return successfully even if i2c device's ->probe()
* callback failed, so check that temperature sensor is functional.
*/
if (qtn_tsensor_get_temperature(qw->se95_temp_sensor, &temp) < 0) {
i2c_unregister_device(qw->se95_temp_sensor);
qw->se95_temp_sensor = NULL;
i2c_put_adapter(adapter);
DBGPRINTF_N("QDRV: no external temperature sensor found\n");
}
}
static void qdrv_txpow_cal_execute(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, tx_power_cal_data.bbrf_cal_work.work);
int temp = 0;
qtn_tsensor_get_temperature(qw->se95_temp_sensor, &temp);
qw->tx_power_cal_data.temp_info.temp_index = qdrv_txpow_cal_convert_temp_index(qw, temp);
qw->tx_power_cal_data.temp_info.real_temp = temp;
if (p_muc_temp_index) {
memcpy(p_muc_temp_index, &qw->tx_power_cal_data.temp_info, sizeof(*p_muc_temp_index));
}
schedule_delayed_work(&qw->tx_power_cal_data.bbrf_cal_work, jiffies + QDRV_TEMP_CAL_PERIOD);
}
static void qdrv_get_internal_temp(struct work_struct *work)
{
struct qdrv_wlan *qw = container_of(work, struct qdrv_wlan, tx_power_cal_data.bbrf_cal_work.work);
int temp = 0;
qw->tx_power_cal_data.temp_info.temp_index = topaz_read_internal_temp_sens(&temp);
qw->tx_power_cal_data.temp_info.real_temp = temp;
if (p_muc_temp_index) {
memcpy(p_muc_temp_index, &qw->tx_power_cal_data.temp_info, sizeof(*p_muc_temp_index));
}
schedule_delayed_work(&qw->tx_power_cal_data.bbrf_cal_work, jiffies + QDRV_TEMP_CAL_PERIOD);
}
static void qdrv_txpow_cal_init(struct qdrv_wlan *qw)
{
int i;
int num_temp_zone = 0;
int default_tpf[DEFAULT_NUM_TEMP_ZONE] = {
40, 46, 52, 57, 63, 67, 70, 74, 77, 81, 85, 89, 92, 96, 100
};
char temperature_profile[MAX_SIZE_TEMP_PROFILE_BUFF] = {0};
int fd_1 = sys_open(TPROFILE_IN_TEMP, O_RDONLY, 0);
int fd_2 = sys_open(TPROFILE_IN_PROC, O_RDONLY, 0);
int fd_3 = sys_open(PDETECTOR_IN_PROC, O_RDONLY, 0);
if (fd_1 < 0 && fd_2 < 0) {
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_WLAN,
"QDRV: using default temperature profile\n");
num_temp_zone = DEFAULT_NUM_TEMP_ZONE;
memcpy(tpf, default_tpf, sizeof(int) * DEFAULT_NUM_TEMP_ZONE);
} else if (fd_1 >= 0 && fd_2 < 0) {
sys_read(fd_1, temperature_profile, MAX_SIZE_TEMP_PROFILE_BUFF);
sys_close(fd_1);
num_temp_zone = qdrv_txpow_cal_tzone_get(temperature_profile);
} else if (fd_1 < 0 && fd_2 >= 0) {
sys_read(fd_2, temperature_profile, MAX_SIZE_TEMP_PROFILE_BUFF);
sys_close(fd_2);
num_temp_zone = qdrv_txpow_cal_tzone_get(temperature_profile);
} else {
sys_read(fd_2, temperature_profile, MAX_SIZE_TEMP_PROFILE_BUFF);
sys_close(fd_2);
num_temp_zone = qdrv_txpow_cal_tzone_get(temperature_profile);
}
for (i = 0; i < num_temp_zone; i ++) {
tpf[i] *= 100000;
}
if (fd_3 < 0) {
if (qw->se95_temp_sensor) {
qw->tx_power_cal_data.temp_info.num_zone = num_temp_zone;
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_WLAN,
"QDRV: temperature sensor zone=%d, <%d, %d, %d, %d>\n",
num_temp_zone, tpf[0], tpf[1], tpf[num_temp_zone - 2],
tpf[num_temp_zone - 1]);
INIT_DELAYED_WORK(&qw->tx_power_cal_data.bbrf_cal_work, qdrv_txpow_cal_execute);
schedule_delayed_work(&qw->tx_power_cal_data.bbrf_cal_work,
jiffies + QDRV_TEMP_CAL_PERIOD);
} else {
DBGPRINTF_W("QDRV: failed to initialize power calibration\n");
}
} else {
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN,
"QDRV: using %s for Tx gain calibration\n", PDETECTOR_IN_PROC);
/*
* If no external temp sensor is found then rx_stats sys_temp
* would be updated from internal temp sensor
*/
INIT_DELAYED_WORK(&qw->tx_power_cal_data.bbrf_cal_work, qdrv_get_internal_temp);
schedule_delayed_work(&qw->tx_power_cal_data.bbrf_cal_work,
jiffies + QDRV_TEMP_CAL_PERIOD);
}
}
static void qdrv_txpow_cal_stop(struct qdrv_wlan *qw)
{
cancel_delayed_work_sync(&qw->tx_power_cal_data.bbrf_cal_work);
}
#if !TOPAZ_FPGA_PLATFORM
static void qdrv_wlan_enable_hr(unsigned long data)
{
struct qdrv_wlan *qw = (struct qdrv_wlan*)data;
qdrv_hostlink_set_hrflags(qw, 1);
}
#endif
/* Enable the one-shot timer to enable hang detection/recovery */
static void qdrv_wlan_hr_oneshot_enable(struct qdrv_wlan *qw)
{
#if !TOPAZ_FPGA_PLATFORM
init_timer(&qw->hr_timer);
qw->hr_timer.function = qdrv_wlan_enable_hr;
qw->hr_timer.data = (unsigned long)qw;
qw->hr_timer.expires = jiffies + QDRV_WLAN_HR_DELAY_SECS * HZ;
add_timer(&qw->hr_timer);
#endif
}
static void
qdrv_wlan_hr_oneshot_disable(struct qdrv_wlan *qw)
{
del_timer(&qw->hr_timer);
}
static struct sk_buff *ip4_multicast_alloc_query(struct net_device *qbr_dev)
{
struct sk_buff *skb;
struct igmphdr *ih;
struct ethhdr *eth;
struct iphdr *iph;
struct in_device *in_dev;
in_dev = in_dev_get(qbr_dev);
if (!in_dev) {
DBGPRINTF_LIMIT_E("could not get inet device\n");
return NULL;
}
skb = netdev_alloc_skb_ip_align(qbr_dev, sizeof(*eth) + sizeof(*iph) +
sizeof(*ih) + 4);
if (!skb)
goto out;
skb->protocol = htons(ETH_P_IP);
skb_reset_mac_header(skb);
eth = eth_hdr(skb);
memcpy(eth->h_source, qbr_dev->dev_addr, 6);
eth->h_dest[0] = 1;
eth->h_dest[1] = 0;
eth->h_dest[2] = 0x5e;
eth->h_dest[3] = 0;
eth->h_dest[4] = 0;
eth->h_dest[5] = 1;
eth->h_proto = htons(ETH_P_IP);
skb_put(skb, sizeof(*eth));
skb_set_network_header(skb, skb->len);
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = 6;
iph->tos = 0xc0;
iph->tot_len = htons(sizeof(*iph) + sizeof(*ih) + 4);
iph->id = 0x0;
iph->frag_off = htons(IP_DF);
iph->ttl = 1;
iph->protocol = IPPROTO_IGMP;
iph->saddr = in_dev->ifa_list->ifa_address;
iph->daddr = htonl(INADDR_ALLHOSTS_GROUP);
((u8 *)&iph[1])[0] = IPOPT_RA;
((u8 *)&iph[1])[1] = 4;
((u8 *)&iph[1])[2] = 0;
((u8 *)&iph[1])[3] = 0;
ip_send_check(iph);
skb_put(skb, 24);
skb_set_transport_header(skb, skb->len);
ih = igmp_hdr(skb);
ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
ih->code = 0xa;
ih->group = 0;
ih->csum = 0;
ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr));
skb_put(skb, sizeof(*ih));
out:
return skb;
}
static void qdrv_wlan_send_to_node(struct ieee80211vap *vap, struct sk_buff *skb)
{
struct qdrv_vap *qv;
struct net_device *vdev;
qv = container_of(vap, struct qdrv_vap, iv);
vdev = qv->ndev;
skb->dev = vdev;
skb->priority = WME_AC_VO;
QTN_SKB_ENCAP(skb) = QTN_SKB_ENCAP_ETH;
M_FLAG_SET(skb, M_NO_AMSDU);
dev_queue_xmit(skb);
}
static void qdrv_wlan_igmp_query_send(struct qdrv_wlan *qw, struct ieee80211vap *vap)
{
struct sk_buff *skb;
if (!qw->br_dev)
return;
skb = ip4_multicast_alloc_query(qw->br_dev);
if (!skb) {
DBGPRINTF_LIMIT_E("could not alloc igmp query skb\n");
return;
}
qdrv_wlan_send_to_node(vap, skb);
}
static void qdrv_wlan_igmp_query_timer_handler(unsigned long data)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)data;
struct ieee80211com *ic = &qw->ic;
struct ieee80211vap *vap;
if ((ic->ic_vendor_fix & VENDOR_FIX_BRCM_AP_GEN_IGMPQUERY) &&
ic->ic_nonqtn_sta) {
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (vap->iv_state != IEEE80211_S_RUN)
continue;
if (vap->iv_non_qtn_sta_assoc == 0)
continue;
qdrv_wlan_igmp_query_send(qw, vap);
}
}
mod_timer(&qw->igmp_query_timer, jiffies + QDRV_WLAN_IGMP_QUERY_INTERVAL * HZ);
}
void qdrv_wlan_igmp_query_timer_start(struct qdrv_wlan *qw)
{
init_timer(&qw->igmp_query_timer);
qw->igmp_query_timer.function = qdrv_wlan_igmp_query_timer_handler;
qw->igmp_query_timer.data = (unsigned long)qw;
qw->igmp_query_timer.expires = jiffies + QDRV_WLAN_IGMP_QUERY_INTERVAL * HZ;
add_timer(&qw->igmp_query_timer);
}
void qdrv_wlan_igmp_timer_stop(struct qdrv_wlan *qw)
{
del_timer(&qw->igmp_query_timer);
}
static int
qdrv_troubleshoot_start_cb(void *in_ctx)
{
struct qdrv_wlan *qw = in_ctx;
/* Stop the MuC so we can borrow its stack */
if (qw) {
struct qdrv_mac *mac = qw->mac;
mac->dead = 1;
mdelay(100);
hal_disable_muc();
}
return 0;
}
static void
qdrv_wlan_debug_init(struct qdrv_wlan *qw)
{
/* Hook into the troubleshoot functions */
arc_set_sram_safe_area(CONFIG_ARC_MUC_STACK_INIT - CONFIG_ARC_MUC_STACK_SIZE, CONFIG_ARC_MUC_STACK_INIT);
arc_set_troubleshoot_start_hook(qdrv_troubleshoot_start_cb, qw);
}
void qdrv_update_cgq_stats(void *ctx, uint32_t type, uint8_t index, uint32_t value)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)ctx;
switch (type) {
case TOPAZ_CONGEST_QUEUE_STATS_QLEN:
qw->cgq_stats.congest_qlen[index] = value;
break;
case TOPAZ_CONGEST_QUEUE_STATS_ENQFAIL:
qw->cgq_stats.congest_enq_fail[index] = value;
break;
default:
break;
}
}
int qdrv_wlan_init(struct qdrv_mac *mac, struct host_ioctl_hifinfo *hifinfo,
u32 arg1, u32 arg2)
{
struct qdrv_wlan *qw; /* Old struct qnet_priv */
int i;
if (!TOPAZ_HBM_SKB_ALLOCATOR_DEFAULT) {
printk(KERN_ERR "%s: wlan rx accelerate should be used with topaz hbm"
"skb allocator only\n", __FUNCTION__);
return -1;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Allocate a wlan structure */
qw = kmalloc(sizeof(*qw), GFP_KERNEL);
if (qw == NULL) {
DBGPRINTF_E("Failed to allocate wlan structure\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_WLAN,
"qw 0x%08x ic 0x%08x\n",
(unsigned int) qw, (unsigned int) &qw->ic);
/* Clean it out */
memset(qw, 0, sizeof(struct qdrv_wlan));
/* Store it in the mac structure as opaque private data */
mac->data = (void *) qw;
qw->flags_ext |= QDRV_WLAN_FLAG_UNKNOWN_ARP;
qw->flags_ext |= QDRV_WLAN_FLAG_AUC_TX;
reg_congest_queue_stats(qdrv_update_cgq_stats, qw);
/* We need the back pointer so we can control interrupts */
qw->mac = mac;
/* Initialize the wlan data structure */
qw->unit = mac->unit;
qw->flags = arg1 & IOCTL_DEVATTACH_DEVFLAG_MASK;
qw->rf_chipid = (arg1 & IOCTL_DEVATTACH_DEV_RFCHIP_FREQID_MASK) >>
IOCTL_DEVATTACH_DEV_RFCHIP_FREQID_MASK_S;
qw->rf_chip_verid = (arg1 & IOCTL_DEVATTACH_DEV_RFCHIP_VERID_MASK) >>
IOCTL_DEVATTACH_DEV_RFCHIP_VERID_MASK_S;
qw->host_sem = (u32)&mac->ruby_sysctrl->l2m_sem;
soc_shared_params->rf_chip_id = qw->rf_chipid;
if ((strcmp(QDRV_CFG_TYPE, "qtm710_rgmii_config") == 0) ||
(strcmp(QDRV_CFG_TYPE, "topaz_rgmii_config") == 0) ||
(strcmp(QDRV_CFG_TYPE, "topaz_vzn_config") == 0) ||
(strcmp(QDRV_CFG_TYPE, "topaz_pcie_config") == 0))
qw->br_isolate = QDRV_BR_ISOLATE_NORMAL;
else
qw->br_isolate = 0;
qw->br_isolate_vid = 0;
#ifdef CONFIG_QUANTENNA_RESTRICT_WLAN_IP
qw->restrict_wlan_ip = 1;
#else
qw->restrict_wlan_ip = 0;
#endif
qw->br_dev = dev_get_by_name(&init_net, "br0");
if (!qw->br_dev)
DBGPRINTF_E("Could not get bridge device\n");
spin_lock_init(&qw->lock);
spin_lock_init(&qw->flowlock);
qdrv_br_create(&qw->bridge_table);
qw->mcs_odd_even = 0;
qw->tx_restrict = 0;
qw->tx_restrict_rts = IEEE80211_TX_RESTRICT_RTS_DEF;
qw->tx_restrict_limit = IEEE80211_TX_RESTRICT_LIMIT_DEF;
qw->tx_restrict_rate = IEEE80211_TX_RESTRICT_RATE;
qw->tx_swretry_agg_max = -1;
qdrv_wlan_tx_sch_init(qw);
qw->tx_swretry_noagg_max = -1;
qw->arp_last_sent = jiffies;
qw->tx_swretry_suspend_xmit = -1;
/* init csa workqueues and irq handlers */
qdrv_init_csa_irqhandler(qw);
INIT_WORK(&qw->csa_wq, csa_work);
spin_lock_init(&qw->csa_lock);
INIT_WORK(&qw->remain_chan_wq, remain_channel_work);
INIT_WORK(&qw->channel_work_wq, channel_work);
qdrv_init_cca_irqhandler(qw);
INIT_WORK(&qw->cca_wq, cca_work);
spin_lock_init(&qw->cca_lock);
qdrv_init_meas_irqhandler(qw);
INIT_WORK(&qw->meas_wq, meas_work);
#ifdef QTN_BG_SCAN
qdrv_init_scan_irqhandler(qw);
INIT_WORK(&qw->scan_wq, scan_work);
spin_lock_init(&qw->scan_lock);
#endif /* QTN_BG_SCAN */
#ifdef CONFIG_QVSP
if (qdrv_wlan_vsp_irq_init(qw, hifinfo->hi_vsp_stats_phys)) {
panic("Could not initialize VSP stats IRQ");
}
#endif
qw->shared_pernode_stats_pool = dma_alloc_coherent(NULL,
sizeof(struct qtn_node_shared_stats_list) * QTN_PER_NODE_STATS_POOL_SIZE,
&qw->shared_pernode_stats_phys, GFP_KERNEL | GFP_DMA | __GFP_ZERO);
if (qw->shared_pernode_stats_pool == NULL) {
DBGPRINTF_E("Failed to allocate per node stats pool");
return -ENOMEM;
}
TAILQ_INIT(&qw->shared_pernode_stats_head);
for (i = 0; i < QTN_PER_NODE_STATS_POOL_SIZE; i++) {
TAILQ_INSERT_TAIL(&qw->shared_pernode_stats_head,
&qw->shared_pernode_stats_pool[i], next);
}
/* Initialize the TX interface */
if (qdrv_tx_init(mac, hifinfo, arg2) < 0) {
DBGPRINTF_E("Failed to initialize TX\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Initialize the RX interface */
if (qdrv_rx_init(qw, hifinfo) < 0) {
DBGPRINTF_E("Failed to initialize RX\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Initialize the Scan interface */
if (qdrv_scan_init(qw, hifinfo) < 0) {
DBGPRINTF_E("Failed to initialize Scan\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Initialize the hostlink interface */
if (qdrv_hostlink_init(qw, hifinfo) < 0) {
DBGPRINTF_E("Failed to initialize hostlink\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Initialize the beamforming support */
if (qdrv_txbf_init(qw) < 0) {
DBGPRINTF_E("Failed to initialize beamforming\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Start the RX interface */
if (qdrv_rx_start(mac) < 0) {
DBGPRINTF_E("Failed to start RX\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Start the TX interface */
if (qdrv_tx_start(mac) < 0) {
DBGPRINTF_E("Failed to start TX\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Start the Scan interface */
if (qdrv_scan_start(mac) < 0) {
DBGPRINTF_E("Failed to start Scan\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Start the hostlink interface */
if (qdrv_hostlink_start(mac) < 0) {
DBGPRINTF_E("Failed to start hostlink\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Set up rate tables for all potential media types. */
if (set_rates(qw, IEEE80211_MODE_11A) < 0) {
DBGPRINTF_E("Failed to set A rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11B) < 0) {
DBGPRINTF_E("Failed to set B rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11G) < 0) {
DBGPRINTF_E("Failed to set G rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11NG) < 0) {
DBGPRINTF_E("Failed to set NG rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11NG_HT40PM) < 0) {
DBGPRINTF_E("Failed to set NG rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11NA) < 0) {
DBGPRINTF_E("Failed to set NA rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11NA_HT40PM) < 0) {
DBGPRINTF_E("Failed to set NA rates\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11AC_VHT20PM) < 0) {
DBGPRINTF_E("Failed to set AC rates VHT20\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11AC_VHT40PM) < 0) {
DBGPRINTF_E("Failed to set AC rates VHT40\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_rates(qw, IEEE80211_MODE_11AC_VHT80PM) < 0) {
DBGPRINTF_E("Failed to set AC rates VHT80\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Initialize the 802.11 layer (old qtn_attach()) */
if (qdrv_wlan_80211_init(&qw->ic, mac->mac_addr, qw->rf_chipid) < 0) {
DBGPRINTF_E("Failed to initialize 802.11 (ieee80211com)\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_mode(qw, IEEE80211_MODE_11G) < 0) {
DBGPRINTF_E("Failed to set mode\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
if (set_mode(qw, IEEE80211_MODE_11NG) < 0) {
DBGPRINTF_E("Failed to set mode\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
qdrv_init_tsensor(qw);
qdrv_txpow_cal_init(qw);
/* start DFS function */
qdrv_radar_init(mac);
qdrv_pktlogger_init(qw);
/* Timer to enable hang recovery. We delay this as the intial channel
* change can take a long time to complete, causing false hangs to be
* detected.
*/
qdrv_wlan_hr_oneshot_enable(qw);
/* Subscribe to PM notifications */
qw->pm_notifier.notifier_call = qdrv_pm_notify;
pm_qos_add_notifier(PM_QOS_POWER_SAVE, &qw->pm_notifier);
qdrv_wlan_debug_init(qw);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
br_fdb_get_active_sub_port_hook = qdrv_get_active_sub_port;
br_fdb_check_active_sub_port_hook = qdrv_check_active_sub_port;
#endif
qw->sp = qtn_mproc_sync_shared_params_get();
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
/* WPS Polling programs */
static u8 qdrv_wps_gpio_polling_pin = 255;
static u32 qdrv_wps_button_last_level;
static u32 qdrv_wps_button_active_level;
int qdrv_wlan_exit(struct qdrv_mac *mac)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) mac->data;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
qdrv_wlan_80211_exit(&qw->ic);
qdrv_wlan_hr_oneshot_disable(qw);
qdrv_txpow_cal_stop(qw);
if (qw->se95_temp_sensor) {
i2c_unregister_device(qw->se95_temp_sensor);
qw->se95_temp_sensor = NULL;
}
qdrv_txbf_exit(qw);
qdrv_hostlink_exit(qw);
qdrv_tx_stop(mac);
qdrv_scan_exit(qw);
qdrv_rx_exit(qw);
qdrv_tx_exit(qw);
qdrv_sch_shared_data_exit(qw->tx_sch_shared_data);
qdrv_pktlogger_exit(qw);
qdrv_br_exit(&qw->bridge_table);
if (qw->br_dev != NULL) {
dev_put(qw->br_dev);
}
if (qw->pktlogger.dev != NULL) {
dev_put(qw->pktlogger.dev);
}
dma_free_coherent(NULL, sizeof(struct qtn_node_shared_stats_list) * QTN_PER_NODE_STATS_POOL_SIZE,
qw->shared_pernode_stats_pool, qw->shared_pernode_stats_phys);
#ifdef CONFIG_QVSP
qdrv_wlan_vsp_irq_exit(qw);
#endif
qdrv_genpcap_exit(qw);
kfree(qw);
/* Reset the MAC data structure */
mac->data = NULL;
if (qdrv_wps_gpio_polling_pin != 255)
gpio_free(qdrv_wps_gpio_polling_pin);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
br_fdb_get_active_sub_port_hook = NULL;
br_fdb_check_active_sub_port_hook = NULL;
#endif
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
/*
* Queue of processes who access wps_button file
*/
DECLARE_WAIT_QUEUE_HEAD(WPS_Button_WaitQ);
/* WPS button event reported to user space process */
typedef enum {
WPS_BUTTON_NONE_EVENT = 0,
WPS_BUTTON_WIRELESS_EVENT,
WPS_BUTTON_DBGDUMP_EVENT,
WPS_BUTTON_INVALIDE_EVENT
} WPS_Button_Event;
#define WPS_BUTTON_VALID(e) (WPS_BUTTON_NONE_EVENT < (e) && (e) < WPS_BUTTON_INVALIDE_EVENT)
static WPS_Button_Event wps_button_event = WPS_BUTTON_NONE_EVENT;
static void qdrv_wps_button_event_wakeup(WPS_Button_Event event)
{
if (!WPS_BUTTON_VALID(event))
return;
wps_button_event = event;
wake_up_all(&WPS_Button_WaitQ);
}
static ssize_t qdrv_wps_button_read(struct device *dev,
struct device_attribute *attr,
char *buff)
{
int i = 0;
/* As usual, this read is always blocked untill wps button is pressed
* so increase the module reference to prevent it being unload during
* blocking read
*/
if (!try_module_get(THIS_MODULE))
return 0;
/* wait for valid WPS button event */
wait_event_interruptible(WPS_Button_WaitQ, WPS_BUTTON_VALID(wps_button_event));
/* read back empty string in signal wakeup case */
for (i = 0; i < _NSIG_WORDS; i++) {
if (current->pending.signal.sig[i] & ~current->blocked.sig[i]) {
module_put(THIS_MODULE);
return 0;
}
}
sprintf(buff, "%d\n", wps_button_event);
/* after new event been handled, reset to none event */
wps_button_event = WPS_BUTTON_NONE_EVENT;
module_put(THIS_MODULE);
return strlen(buff);
}
DEVICE_ATTR(wps_button, S_IRUSR, qdrv_wps_button_read, NULL); /* dev_attr_wps_button */
static inline void qdrv_wps_button_device_file_create(struct net_device *ndev)
{
device_create_file(&(ndev->dev), &dev_attr_wps_button);
}
static inline void qdrv_wps_button_device_file_remove(struct net_device *ndev)
{
device_remove_file(&ndev->dev, &dev_attr_wps_button);
}
/* records the jiffies when button down, back to 0 after button released */
static u32 qdrv_wps_button_down_jiffies = 0;
static int interrupt_mode = 0;
#define WPS_BUTTON_TIMER_INTERVAL ((3 * HZ) / 10) /* timer internal */
static void qdrv_wps_polling_button_notifier(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
u32 current_level;
current_level = gpio_get_value(qdrv_wps_gpio_polling_pin);
/* records the falling edge jiffies */
if ((current_level == qdrv_wps_button_active_level)
&& (qdrv_wps_button_last_level != qdrv_wps_button_active_level)) {
qdrv_wps_button_down_jiffies = jiffies;
}
/* at rising edge */
if ((current_level != qdrv_wps_button_active_level)
&& (qdrv_wps_button_last_level == qdrv_wps_button_active_level)) {
/* WPS button event is rising triggered -- when button
* being changed from active to inactive level.
*
* Different press time trigger different event
*/
if ((jiffies - qdrv_wps_button_down_jiffies) >= 10 * HZ) {
/* wakeup the event waiting processes */
qdrv_wps_button_event_wakeup(WPS_BUTTON_DBGDUMP_EVENT);
DBGPRINTF_N("WPS: button long press polling at %u\n", (unsigned int) jiffies);
} else {
/* wakeup the event waiting processes */
qdrv_wps_button_event_wakeup(WPS_BUTTON_WIRELESS_EVENT);
qdrv_eventf(dev, "WPS-BUTTON.indication");
DBGPRINTF_N("WPS: button short press polling at %u\n", (unsigned int) jiffies);
}
/* back to 0 after rising edge */
qdrv_wps_button_down_jiffies = 0;
if (interrupt_mode)
goto interrupt_end;
}
/* Restart the timer */
mod_timer(&qdrv_wps_button_timer, jiffies + WPS_BUTTON_TIMER_INTERVAL);
interrupt_end:
qdrv_wps_button_last_level = current_level;
return;
}
static int qdrv_polling_wps_button_init(struct net_device *dev, u8 wps_gpio_pin, u8 active_logic, int mode)
{
interrupt_mode = mode;
if (wps_gpio_pin > MAX_GPIO_PIN) {
DBGPRINTF_E("WPS polling GPIO pin %d is invalid\n", wps_gpio_pin);
return -1;
}
/*
* Set up timer to poll the button.
* Request the GPIO resource and export it for userspace
*/
if (gpio_request(wps_gpio_pin, dev->name) < 0)
DBGPRINTF_E("%s: Failed to request GPIO%d for GPIO reset\n",
dev->name, wps_gpio_pin);
else
gpio_export(wps_gpio_pin, true);
qdrv_wps_gpio_polling_pin = wps_gpio_pin;
qdrv_wps_button_active_level = (active_logic) ? 1 : 0;
qdrv_wps_button_last_level = ~qdrv_wps_button_active_level;
init_timer(&qdrv_wps_button_timer);
qdrv_wps_button_timer.function = qdrv_wps_polling_button_notifier;
qdrv_wps_button_timer.data = (unsigned long)dev;
/* creeate the device file for user space use */
qdrv_wps_button_device_file_create(dev);
return 0;
}
static void qdrv_polling_wps_button_begin(void)
{
qdrv_wps_button_timer.expires = jiffies + WPS_BUTTON_TIMER_INTERVAL;
if (!timer_pending(&qdrv_wps_button_timer))
add_timer(&qdrv_wps_button_timer);
}
static void qdrv_polling_wps_button_exit(uint8_t wps_gpio_pin)
{
struct net_device *ndev = (struct net_device *)(qdrv_wps_button_timer.data);
qdrv_wps_button_device_file_remove(ndev);
del_timer_sync(&qdrv_wps_button_timer);
qdrv_wps_button_timer.data = (unsigned long)(NULL);
gpio_free(wps_gpio_pin);
}
static irqreturn_t qdrv_wps_button_handler(int irq, void *dev_id)
{
qdrv_polling_wps_button_begin();
return IRQ_HANDLED;
}
static int
qdrv_interrupt_wps_button_init(struct net_device *dev, u8 wps_gpio_pin)
{
u8 active_logic;
/* current wps button is in released state, so its value can determine active_logic */
active_logic = (~gpio_get_value(wps_gpio_pin) & 0x01) ? 1 : 0;
if (request_irq(GPIO2IRQ(wps_gpio_pin), qdrv_wps_button_handler, IRQF_TRIGGER_FALLING | IRQF_SHARED,
"qwps_btn", dev)) {
DBGPRINTF_E("WPS: push button IRQ %d is not free for register falling edge irq\n", GPIO2IRQ(wps_gpio_pin));
return -1;
}
DBGPRINTF_N("WPS: push button IRQ initialised\n");
return qdrv_polling_wps_button_init(dev, wps_gpio_pin, active_logic, 1);
}
static void qdrv_interrupt_wps_button_exit(struct net_device *dev, u8 wps_gpio_pin)
{
qdrv_polling_wps_button_exit(wps_gpio_pin);
free_irq(GPIO2IRQ(wps_gpio_pin), dev);
}
int qdrv_wps_button_init(struct net_device *dev)
{
u8 wps_gpio_pin = 0;
u8 use_interrupt = 0;
u8 active_logic = 0;
int retval;
if (qdrv_get_wps_push_button_config(&wps_gpio_pin, &use_interrupt, &active_logic) != 0) {
DBGPRINTF_N("WPS: push button is not configured\n");
return 0;
}
DBGPRINTF_N("WPS: push button GPIO pin %d\n", wps_gpio_pin);
DBGPRINTF_N("WPS: monitored using %s\n", use_interrupt ? "interrupt" : "polling");
if (use_interrupt) {
DBGPRINTF_N("WPS: interrupt on line %d\n", GPIO2IRQ(wps_gpio_pin));
} else {
DBGPRINTF_N("WPS: active logic is %s\n", active_logic ? "high" : "low");
}
set_wps_push_button_enabled();
if (use_interrupt) {
retval = qdrv_interrupt_wps_button_init(dev, wps_gpio_pin);
} else {
retval = qdrv_polling_wps_button_init(dev, wps_gpio_pin, active_logic, 0);
qdrv_polling_wps_button_begin();
}
return retval;
}
void qdrv_wps_button_exit(void)
{
struct net_device *dev = (struct net_device *)(qdrv_wps_button_timer.data);
u8 wps_gpio_pin = 0;
u8 use_interrupt = 0;
u8 active_logic = 0;
if (!dev)
return;
if (qdrv_get_wps_push_button_config(&wps_gpio_pin, &use_interrupt, &active_logic) != 0) {
return;
}
if (use_interrupt) {
qdrv_interrupt_wps_button_exit(dev, wps_gpio_pin);
} else {
qdrv_polling_wps_button_exit(wps_gpio_pin);
}
}
struct net_device *qdrv_wps_button_get_dev(void)
{
return (struct net_device *)(qdrv_wps_button_timer.data);
}
/**
* Intermediate point between MUC and WLAN driver. Layer to hide 802.11 specific structures
* from the MUC comm layer.
*
* @return 1 if the MIC failure was reported to the WLAN driver, 0 otherwise.
*/
int qdrv_wlan_tkip_mic_error(struct qdrv_mac *mac, int devid, int count)
{
struct net_device *dev = mac->vnet[QDRV_WLANID_FROM_DEVID(devid)];
struct ieee80211vap *vap;
struct ieee80211com *ic;
if (dev) {
vap = netdev_priv(dev);
ic = vap->iv_ic;
ic->ic_tkip_mic_failure(vap, count);
return 1;
}
return 0;
}
/* record channel change event */
void qdrv_channel_switch_record(struct ieee80211com *ic,
struct ieee80211_channel *new_chan,
uint32_t reason)
{
struct ieee80211req_csw_record *records = &ic->ic_csw_record;
int index = records->index;
qdrv_chan_occupy_record_start(ic, new_chan->ic_ieee);
/* if the new_chan is same with last channel, do not record it */
if (new_chan->ic_ieee == records->channel[index]) {
return;
}
if (records->cnt < CSW_MAX_RECORDS_MAX) {
records->cnt++;
}
if (records->cnt == 1) {
records->index = 0;
} else {
if (records->index == (CSW_MAX_RECORDS_MAX - 1))
records->index = 0;
else
records->index++;
}
index = records->index;
records->channel[index] = new_chan->ic_ieee;
records->timestamp[index] = (jiffies - INITIAL_JIFFIES) / HZ;
if (ic->ic_opmode != IEEE80211_M_STA) {
records->reason[index] = reason;
if ((reason & CSW_REASON_MASK) == IEEE80211_CSW_REASON_SCS) {
records->reason[index] = ic->ic_csw_reason;
memcpy(records->csw_record_mac[index], ic->ic_csw_mac, IEEE80211_ADDR_LEN);
}
} else {
records->reason[index] = IEEE80211_CSW_REASON_UNKNOWN;
}
}
EXPORT_SYMBOL(qdrv_channel_switch_record);
void qdrv_channel_switch_reason_record(struct ieee80211com *ic, int reason_flag)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
switch (reason_flag & CSW_REASON_MASK) {
case IEEE80211_CSW_REASON_SCS:
qw->csw_stats.csw_by_scs++;
break;
case IEEE80211_CSW_REASON_DFS:
qw->csw_stats.csw_by_dfs++;
break;
case IEEE80211_CSW_REASON_MANUAL:
qw->csw_stats.csw_by_user++;
break;
case IEEE80211_CSW_REASON_SAMPLING:
qw->csw_stats.csw_by_sampling++;
break;
case IEEE80211_CSW_REASON_TDLS_CS:
qw->csw_stats.csw_by_tdls++;
break;
case IEEE80211_CSW_REASON_BGSCAN:
qw->csw_stats.csw_by_bgscan++;
break;
case IEEE80211_CSW_REASON_OCAC:
qw->csw_stats.csw_by_ocac++;
break;
case IEEE80211_CSW_REASON_OCAC_RUN:
qw->csw_stats.csw_by_ocac_run++;
break;
case IEEE80211_CSW_REASON_CSA:
qw->csw_stats.csw_by_csa++;
break;
case IEEE80211_CSW_REASON_SCAN:
qw->csw_stats.csw_by_scan++;
break;
case IEEE80211_CSW_REASON_COC:
qw->csw_stats.csw_by_coc++;
break;
default:
DBGPRINTF_E("unexpected event\n");
}
}
void qdrv_wlan_drop_ba(struct ieee80211_node *ni, int tid, int tx, int reason)
{
ieee80211_send_delba(ni, tid, !tx, reason);
ieee80211_node_ba_del(ni, tid, tx, reason);
}
void qdrv_wlan_dump_ba(struct ieee80211_node *ni)
{
int32_t tid;
int istx;
struct ieee80211_ba_tid *ba_tid;
printk("Node %u %pM BA table:\n", IEEE80211_AID(ni->ni_associd), ni->ni_macaddr);
printk("tx tid state type win timeout\n");
for (istx = 0; istx <= 1; istx++) {
for (tid = 0; tid < WME_NUM_TID; tid++) {
ba_tid = istx ? &ni->ni_ba_tx[tid] : &ni->ni_ba_rx[tid];
printk("%2d %3d %5d %4d %3d %7d\n",
istx, tid, ba_tid->state,
ba_tid->type, ba_tid->buff_size, ba_tid->timeout);
}
}
}
static void qdrv_wlan_set_11g_erp(struct ieee80211vap *vap, int on)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_node_args *args = NULL;
dma_addr_t args_dma;
if (!(ioctl = vnet_alloc_ioctl(qv)) ||
!(args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC))) {
DBGPRINTF_E("Failed to allocate set_11g_erp message\n");
vnet_free_ioctl(ioctl);
return;
}
memset(args, 0, sizeof(*args));
ioctl->ioctl_command = IOCTL_DEV_SET_11G_ERP;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_arg2 = on;
ioctl->ioctl_argp = args_dma;
vnet_send_ioctl(qv, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
}
void qdrv_wlan_cleanup_before_reload(struct ieee80211com *ic)
{
ic->ic_chan_is_set = 0;
}
int8_t qdrv_get_local_tx_power(struct ieee80211com *ic)
{
#define QTN_TXPOW_TOTAL_OFFSET 6
if (qdrv_is_gain_low()) {
return IEEE80211_LOWGAIN_TXPOW_MAX + QTN_TXPOW_TOTAL_OFFSET;
} else {
return ic->ic_curchan->ic_maxpower_normal + QTN_TXPOW_TOTAL_OFFSET;
}
#undef QTN_TXPOW_TOTAL_OFFSET
}
static int8_t min_rssi_40MHZ_perchain_mcstbl[] = {
-82,
-82,
-81,
-78,
-74,
-71,
-70,
-68,
-82,
-80,
-77,
-74,
-71,
-67,
-65,
-63,
-80,
-77,
-73,
-71,
-66,
-63,
-61,
-58,
-72,
-69,
-63,
-61,
-55,
-47,
-47,
-47,
-76,
-71,
-72,
-70,
-65,
-65,
-74,
-72,
-70,
-70,
-69,
-67,
-67,
-68,
-68,
-62,
-61,
-63,
-62,
-62,
-66,
-64,
-62,
-61,
-61,
-60,
-59,
-55,
-57,
-47,
-47,
-47,
-58,
-57,
-57,
-47,
-47,
-47,
-47,
-47,
-47,
-47,
-47,
-47
};
char *link_margin_info_err_msg[] = {
"Link Margin ERROR:No such node in macfw\n" /* QTN_LINK_MARGIN_REASON_NOSUCHNODE*/
};
int qdrv_get_local_link_margin(struct ieee80211_node *ni, int8_t *result)
{
struct qdrv_vap *qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
struct host_ioctl *ioctl;
struct qtn_link_margin_info *lm_info;
dma_addr_t lm_dma;
if ((!(ioctl = vnet_alloc_ioctl(qv))) ||
(!(lm_info = (struct qtn_link_margin_info *)qdrv_hostlink_alloc_coherent(NULL,
sizeof(struct qtn_link_margin_info),
&lm_dma,
GFP_DMA | GFP_ATOMIC)))) {
DBGPRINTF_E("Failed to allocate LINKMARGIN message\n");
vnet_free_ioctl(ioctl);
return -EINVAL;
}
memset(lm_info, 0, sizeof(*lm_info));
memcpy(lm_info->mac_addr, ni->ni_macaddr, 6);
ioctl->ioctl_command = IOCTL_DEV_GET_LINK_MARGIN_INFO;
ioctl->ioctl_arg1 = qv->devid;
ioctl->ioctl_argp = lm_dma;
vnet_send_ioctl(qv, ioctl);
if (lm_info->reason == QTN_LINK_MARGIN_REASON_SUCC) {
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "get link margin info success:bw=%d,mcs=%d,rssi_avg=%d\n",
lm_info->bw,
lm_info->mcs,
lm_info->rssi_avg / 10);
/* protect invalid mcs */
if (lm_info->mcs >= ARRAY_SIZE(min_rssi_40MHZ_perchain_mcstbl))
lm_info->mcs = 0;
/* it seems bw=0 appears usually */
if (!lm_info->bw)
*result = (lm_info->rssi_avg / 10) - RSSI_40M_TO_20M_DBM(min_rssi_40MHZ_perchain_mcstbl[lm_info->mcs]);
else
*result = (lm_info->rssi_avg / 10) - min_rssi_40MHZ_perchain_mcstbl[lm_info->mcs];
} else {
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "%s", link_margin_info_err_msg[lm_info->reason - QTN_LINK_MARGIN_REASON_NOSUCHNODE]);
*result = LINK_MARGIN_INVALID;
}
qdrv_hostlink_free_coherent(NULL, sizeof(struct qtn_link_margin_info), lm_info, lm_dma);
return 0;
}
int qdrv_wlan_get_shared_vap_stats(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
uint8_t vapid = QDRV_WLANID_FROM_DEVID(qv->devid);
qtn_shared_vap_stats_t* shared_stats = qdrv_auc_get_vap_stats(vapid);
if (!shared_stats)
return -EINVAL;
vap->iv_devstats.rx_packets = shared_stats->qtn_rx_pkts;
vap->iv_devstats.rx_bytes = shared_stats->qtn_rx_bytes;
vap->iv_devstats.rx_unicast_packets = shared_stats->qtn_rx_ucast;
vap->iv_devstats.rx_broadcast_packets = shared_stats->qtn_rx_bcast;
vap->iv_devstats.multicast = shared_stats->qtn_rx_mcast;
vap->iv_devstats.rx_dropped = shared_stats->qtn_rx_dropped;
vap->iv_devstats.tx_packets = shared_stats->qtn_tx_pkts;
vap->iv_devstats.tx_bytes = shared_stats->qtn_tx_bytes;
vap->iv_devstats.tx_multicast_packets += shared_stats->qtn_tx_mcast +
shared_stats->qtn_muc_tx_mcast;
vap->iv_devstats.tx_unicast_packets = shared_stats->qtn_tx_pkts -
vap->iv_devstats.tx_multicast_packets -
vap->iv_devstats.tx_broadcast_packets;
vap->iv_devstats.tx_dropped = shared_stats->qtn_tx_dropped;
return 0;
}
int qdrv_wlan_reset_shared_vap_stats(struct ieee80211vap *vap)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
uint8_t vapid = QDRV_WLANID_FROM_DEVID(qv->devid);
qtn_shared_vap_stats_t* shared_stats = qdrv_auc_get_vap_stats(vapid);
if (!shared_stats)
return -EINVAL;
memset(shared_stats, 0, sizeof(*shared_stats));
return 0;
}
int qdrv_wlan_get_shared_node_stats(struct ieee80211_node *ni)
{
uint8_t node_idx = IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx);
qtn_shared_node_stats_t* shared_stats = qdrv_auc_get_node_stats(node_idx);
uint8_t i;
if (!shared_stats)
return -EINVAL;
ni->ni_stats.ns_rx_data = shared_stats->qtn_rx_pkts;
ni->ni_stats.ns_rx_bytes = shared_stats->qtn_rx_bytes;
ni->ni_stats.ns_rx_ucast = shared_stats->qtn_rx_ucast;
ni->ni_stats.ns_rx_mcast = shared_stats->qtn_rx_mcast;
ni->ni_stats.ns_rx_bcast = shared_stats->qtn_rx_bcast;
ni->ni_stats.ns_rx_vlan_pkts = shared_stats->qtn_rx_vlan_pkts;
ni->ni_stats.ns_tx_data = shared_stats->qtn_tx_pkts;
ni->ni_stats.ns_tx_bytes = shared_stats->qtn_tx_bytes;
ni->ni_stats.ns_tx_mcast = shared_stats->qtn_tx_mcast +
shared_stats->qtn_muc_tx_mcast;
ni->ni_stats.ns_tx_ucast = shared_stats->qtn_tx_pkts -
ni->ni_stats.ns_tx_mcast -
ni->ni_stats.ns_tx_bcast;
for (i = 0; i < WMM_AC_NUM; i++)
ni->ni_stats.ns_tx_wifi_drop[i] = shared_stats->qtn_tx_drop_data_msdu[i];
return 0;
}
int qdrv_wlan_reset_shared_node_stats(struct ieee80211_node *ni)
{
uint8_t node_idx = IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx);
qtn_shared_node_stats_t* shared_stats = qdrv_auc_get_node_stats(node_idx);
if (!shared_stats)
return -EINVAL;
memset(shared_stats, 0, sizeof(*shared_stats));
return 0;
}
int qdrv_rxgain_params(struct ieee80211com *ic, int index, struct qtn_rf_rxgain_params *params)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qdrv_hostlink_rxgain_params(qw, index, params);
return 0;
}
void
qdrv_wlan_vlan_enable(struct ieee80211com *ic, int enable)
{
struct qdrv_wlan *qw = container_of(ic, struct qdrv_wlan, ic);
qdrv_hostlink_vlan_enable(qw, enable);
}
int qdrv_wlan_80211_set_bcn_scheme(struct ieee80211vap *vap, int param, int value)
{
struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv);
struct ieee80211com *ic = vap->iv_ic;
int ret = 0;
ret = qdrv_hostlink_change_bcn_scheme(qv, param, value);
if ((ret < 0) || (ret & (QTN_HLINK_RC_ERR)))
return -1;
ic->ic_beaconing_scheme = value;
return 0;
}