Merge git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next

John W. Linville says:

====================
pull request: wireless-next 2014-07-03

Please pull this first batch of wireless updates intended for the
3.17 stream...

For the mac80211 bits, Johannes says:

"The biggest thing here is probably Arik's TDLS rework, beyond that we
have smaller improvements and features like David's scanning IE thing,
Luca's queue work, some CSA work, etc. Also your PID rate control
removal, of course."

For the iwlwifi bits, Emmanuel says:

"I have here a whole bunch of various things. Andy contributes
better debug prints for dvm specific flows and a module parameter to
completely disable power save for dvm. Andrei is sharing the premises
of his work on CSA - more to come. Eran and Liad keep on working
on the new devices. I have the regular amount of BT Coex stuff and
I continue to work on the firmware error report system adding more
debug capabilities. More to come on that subject too."

On top of that, there are some cleanups to the new rsi driver, some
continuing improvements to the rtl818x drivers, and the usual bundles
of updates to ath9k, b43, mwifiex, wil6210, and a few other bits here
and there.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-07-08 14:20:31 -07:00
commit 72948cdcbb
256 changed files with 11883 additions and 4533 deletions

View file

@ -5622,16 +5622,6 @@ F: Documentation/networking/mac80211-injection.txt
F: include/net/mac80211.h
F: net/mac80211/
MAC80211 PID RATE CONTROL
M: Stefano Brivio <stefano.brivio@polimi.it>
M: Mattias Nissler <mattias.nissler@gmx.de>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
S: Maintained
F: net/mac80211/rc80211_pid*
MACVLAN DRIVER
M: Patrick McHardy <kaber@trash.net>
L: netdev@vger.kernel.org

View file

@ -3,6 +3,7 @@ bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
bcma-y += driver_pci.o
bcma-y += driver_pcie2.o
bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o

View file

@ -220,6 +220,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
#endif
switch (cc->core->bus->chipinfo.id) {
case BCMA_CHIP_ID_BCM5357:
case BCMA_CHIP_ID_BCM53572:
chip->ngpio = 32;
break;
default:

175
drivers/bcma/driver_pcie2.c Normal file
View file

@ -0,0 +1,175 @@
/*
* Broadcom specific AMBA
* PCIe Gen 2 Core
*
* Copyright 2014, Broadcom Corporation
* Copyright 2014, Rafał Miłecki <zajec5@gmail.com>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "bcma_private.h"
#include <linux/bcma/bcma.h>
/**************************************************
* R/W ops.
**************************************************/
#if 0
static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr)
{
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR);
return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
}
#endif
static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr,
u32 val)
{
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val);
}
/**************************************************
* Init.
**************************************************/
static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2,
bool enable)
{
u32 val;
/* restore back to default */
val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
val |= PCIE2_CLKC_DLYPERST;
val &= ~PCIE2_CLKC_DISSPROMLD;
if (enable) {
val &= ~PCIE2_CLKC_DLYPERST;
val |= PCIE2_CLKC_DISSPROMLD;
}
pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val);
/* flush */
return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
}
static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2)
{
/* LTR0 */
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c);
/* LTR1 */
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864);
/* LTR2 */
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003);
}
static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2)
{
u8 core_rev = pcie2->core->id.rev;
u32 devstsctr2;
if (core_rev < 2 || core_rev == 10 || core_rev > 13)
return;
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
PCIE2_CAP_DEVSTSCTRL2_OFFSET);
devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) {
/* force the right LTR values */
bcma_core_pcie2_set_ltr_vals(pcie2);
/* TODO:
si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */
/* enable the LTR */
devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB;
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
PCIE2_CAP_DEVSTSCTRL2_OFFSET);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2);
/* set the LTR state to be active */
pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
PCIE2_LTR_ACTIVE);
usleep_range(1000, 2000);
/* set the LTR state to be sleep */
pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
PCIE2_LTR_SLEEP);
usleep_range(1000, 2000);
}
}
static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2)
{
u8 core_rev = pcie2->core->id.rev;
bool pciewar160, pciewar162;
pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11;
pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 ||
core_rev == 9 || core_rev == 11;
if (!pciewar160 && !pciewar162)
return;
/* TODO */
#if 0
pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL,
PCIE_DISABLE_L1CLK_GATING);
#if 0
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
PCIEGEN2_COE_PVT_TL_CTRL_0);
pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA,
~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT));
#endif
#endif
}
static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2)
{
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP);
pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f);
}
static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2)
{
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0);
}
static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2)
{
struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc;
u8 core_rev = pcie2->core->id.rev;
u32 alp_khz, pm_value;
if (core_rev <= 13) {
alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000;
pm_value = (1000000 * 2) / alp_khz;
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
PCIE2_PVT_REG_PM_CLK_PERIOD);
pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value);
}
}
void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
{
struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo;
u32 tmp;
tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54));
if ((tmp & 0xe) >> 1 == 2)
bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17);
/* TODO: Do we need pcie_reqsize? */
if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3)
bcma_core_pcie2_war_delay_perst_enab(pcie2, true);
bcma_core_pcie2_hw_ltr_war(pcie2);
pciedev_crwlpciegen2(pcie2);
pciedev_reg_pm_clk_period(pcie2);
pciedev_crwlpciegen2_180(pcie2);
pciedev_crwlpciegen2_182(pcie2);
}

View file

@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
case BCMA_CORE_CHIPCOMMON:
case BCMA_CORE_PCI:
case BCMA_CORE_PCIE:
case BCMA_CORE_PCIE2:
case BCMA_CORE_MIPS_74K:
case BCMA_CORE_4706_MAC_GBIT_COMMON:
continue;
@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_pci_init(&bus->drv_pci[1]);
}
/* Init PCIe Gen 2 core */
core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0);
if (core) {
bus->drv_pcie2.core = core;
bcma_core_pcie2_init(&bus->drv_pcie2);
}
/* Init GBIT MAC COMMON core */
core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
if (core) {

View file

@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work)
static int at76_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct cfg80211_scan_request *req = &hw_req->req;
struct at76_priv *priv = hw->priv;
struct at76_req_scan scan;
u8 *ssid = NULL;

View file

@ -63,6 +63,7 @@ enum ath_op_flags {
ATH_OP_PRIM_STA_VIF,
ATH_OP_HW_RESET,
ATH_OP_SCANNING,
ATH_OP_MULTI_CHANNEL,
};
enum ath_bus_type {

View file

@ -3137,10 +3137,11 @@ exit:
static int ath10k_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct cfg80211_scan_request *req = &hw_req->req;
struct wmi_start_scan_arg arg;
int ret = 0;
int i;

View file

@ -1285,6 +1285,7 @@ struct ath5k_hw {
#define ATH_STAT_STARTED 3 /* opened & irqs enabled */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int fif_filter_flags; /* Current FIF_* filter flags */
struct ieee80211_channel *curchan; /* current h/w channel */
u16 nvifs;

View file

@ -1382,6 +1382,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
rxs->flag = 0;
if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
rxs->flag |= RX_FLAG_MMIC_ERROR;
if (unlikely(rs->rs_status & AR5K_RXERR_CRC))
rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
/*
* always extend the mac timestamp, since this information is
@ -1449,6 +1452,8 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
ah->stats.rx_bytes_count += rs->rs_datalen;
if (unlikely(rs->rs_status)) {
unsigned int filters;
if (rs->rs_status & AR5K_RXERR_CRC)
ah->stats.rxerr_crc++;
if (rs->rs_status & AR5K_RXERR_FIFO)
@ -1457,7 +1462,20 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
ah->stats.rxerr_phy++;
if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
ah->stats.rxerr_phy_code[rs->rs_phyerr]++;
return false;
/*
* Treat packets that underwent a CCK or OFDM reset as having a bad CRC.
* These restarts happen when the radio resynchronizes to a stronger frame
* while receiving a weaker frame. Here we receive the prefix of the weak
* frame. Since these are incomplete packets, mark their CRC as invalid.
*/
if (rs->rs_phyerr == AR5K_RX_PHY_ERROR_OFDM_RESTART ||
rs->rs_phyerr == AR5K_RX_PHY_ERROR_CCK_RESTART) {
rs->rs_status |= AR5K_RXERR_CRC;
rs->rs_status &= ~AR5K_RXERR_PHY;
} else {
return false;
}
}
if (rs->rs_status & AR5K_RXERR_DECRYPT) {
/*
@ -1480,8 +1498,15 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
return true;
}
/* reject any frames with non-crypto errors */
if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
/*
* Reject any frames with non-crypto errors, and take into account the
* current FIF_* filters.
*/
filters = AR5K_RXERR_DECRYPT;
if (ah->fif_filter_flags & FIF_FCSFAIL)
filters |= AR5K_RXERR_CRC;
if (rs->rs_status & ~filters)
return false;
}

View file

@ -473,6 +473,8 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
/* Set the cached hw filter flags, this will later actually
* be set in HW */
ah->filter_flags = rfilt;
/* Store current FIF filter flags */
ah->fif_filter_flags = *new_flags;
mutex_unlock(&ah->lock);
}

View file

@ -5,7 +5,8 @@ ath9k-y += beacon.o \
recv.o \
xmit.o \
link.o \
antenna.o
antenna.o \
channel.o
ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
ath9k-$(CONFIG_ATH9K_PCI) += pci.o

View file

@ -3535,7 +3535,8 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz)
{
int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl;
if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah))
if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
AR_SREV_9531(ah))
REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah))
REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);

View file

@ -314,10 +314,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
qca953x_1p0_mac_core);
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
qca953x_1p0_mac_postamble);
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
qca953x_1p0_baseband_core);
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
qca953x_1p0_baseband_postamble);
if (AR_SREV_9531_20(ah)) {
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
qca953x_2p0_baseband_core);
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
qca953x_2p0_baseband_postamble);
} else {
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
qca953x_1p0_baseband_core);
INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
qca953x_1p0_baseband_postamble);
}
INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
qca953x_1p0_radio_core);
INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],

View file

@ -1552,13 +1552,15 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
u8 *ini_reloaded)
{
unsigned int regWrites = 0;
u32 modesIndex;
u32 modesIndex, txgain_index;
if (IS_CHAN_5GHZ(chan))
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
else
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
txgain_index = AR_SREV_9531(ah) ? 1 : modesIndex;
if (modesIndex == ah->modes_index) {
*ini_reloaded = false;
goto set_rfmode;
@ -1573,7 +1575,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant,
modesIndex);
REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
REG_WRITE_ARRAY(&ah->iniModesTxGain, txgain_index, regWrites);
if (AR_SREV_9462_20_OR_LATER(ah)) {
/*

View file

@ -219,7 +219,7 @@ static const u32 qca953x_1p0_baseband_core[][2] = {
{0x00009d04, 0x40206c10},
{0x00009d08, 0x009c4060},
{0x00009d0c, 0x9883800a},
{0x00009d10, 0x01884061},
{0x00009d10, 0x018848c6},
{0x00009d14, 0x00c0040b},
{0x00009d18, 0x00000000},
{0x00009e08, 0x0038230c},
@ -715,4 +715,203 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = {
{0x00016448, 0x6c927a70},
};
static const u32 qca953x_2p0_baseband_core[][2] = {
/* Addr allmodes */
{0x00009800, 0xafe68e30},
{0x00009804, 0xfd14e000},
{0x00009808, 0x9c0a9f6b},
{0x0000980c, 0x04900000},
{0x00009814, 0x0280c00a},
{0x00009818, 0x00000000},
{0x0000981c, 0x00020028},
{0x00009834, 0x6400a190},
{0x00009838, 0x0108ecff},
{0x0000983c, 0x14000600},
{0x00009880, 0x201fff00},
{0x00009884, 0x00001042},
{0x000098a4, 0x00200400},
{0x000098b0, 0x32840bbe},
{0x000098bc, 0x00000002},
{0x000098d0, 0x004b6a8e},
{0x000098d4, 0x00000820},
{0x000098dc, 0x00000000},
{0x000098f0, 0x00000000},
{0x000098f4, 0x00000000},
{0x00009c04, 0xff55ff55},
{0x00009c08, 0x0320ff55},
{0x00009c0c, 0x00000000},
{0x00009c10, 0x00000000},
{0x00009c14, 0x00046384},
{0x00009c18, 0x05b6b440},
{0x00009c1c, 0x00b6b440},
{0x00009d00, 0xc080a333},
{0x00009d04, 0x40206c10},
{0x00009d08, 0x009c4060},
{0x00009d0c, 0x9883800a},
{0x00009d10, 0x018848c6},
{0x00009d14, 0x00c0040b},
{0x00009d18, 0x00000000},
{0x00009e08, 0x0038230c},
{0x00009e24, 0x990bb515},
{0x00009e28, 0x0c6f0000},
{0x00009e30, 0x06336f77},
{0x00009e34, 0x6af6532f},
{0x00009e38, 0x0cc80c00},
{0x00009e40, 0x0d261820},
{0x00009e4c, 0x00001004},
{0x00009e50, 0x00ff03f1},
{0x00009fc0, 0x813e4788},
{0x00009fc4, 0x0001efb5},
{0x00009fcc, 0x40000014},
{0x00009fd0, 0x02993b93},
{0x0000a20c, 0x00000000},
{0x0000a220, 0x00000000},
{0x0000a224, 0x00000000},
{0x0000a228, 0x10002310},
{0x0000a23c, 0x00000000},
{0x0000a244, 0x0c000000},
{0x0000a248, 0x00000140},
{0x0000a2a0, 0x00000007},
{0x0000a2c0, 0x00000007},
{0x0000a2c8, 0x00000000},
{0x0000a2d4, 0x00000000},
{0x0000a2ec, 0x00000000},
{0x0000a2f0, 0x00000000},
{0x0000a2f4, 0x00000000},
{0x0000a2f8, 0x00000000},
{0x0000a344, 0x00000000},
{0x0000a34c, 0x00000000},
{0x0000a350, 0x0000a000},
{0x0000a364, 0x00000000},
{0x0000a370, 0x00000000},
{0x0000a390, 0x00000001},
{0x0000a394, 0x00000444},
{0x0000a398, 0x001f0e0f},
{0x0000a39c, 0x0075393f},
{0x0000a3a0, 0xb79f6427},
{0x0000a3a4, 0x000400ff},
{0x0000a3a8, 0x6a6a6a6a},
{0x0000a3ac, 0x6a6a6a6a},
{0x0000a3b0, 0x00c8641a},
{0x0000a3b4, 0x0000001a},
{0x0000a3b8, 0x0088642a},
{0x0000a3bc, 0x000001fa},
{0x0000a3c0, 0x20202020},
{0x0000a3c4, 0x22222220},
{0x0000a3c8, 0x20200020},
{0x0000a3cc, 0x20202020},
{0x0000a3d0, 0x20202020},
{0x0000a3d4, 0x20202020},
{0x0000a3d8, 0x20202020},
{0x0000a3dc, 0x20202020},
{0x0000a3e0, 0x20202020},
{0x0000a3e4, 0x20202020},
{0x0000a3e8, 0x20202020},
{0x0000a3ec, 0x20202020},
{0x0000a3f0, 0x00000000},
{0x0000a3f4, 0x00000000},
{0x0000a3f8, 0x0c9bd380},
{0x0000a3fc, 0x000f0f01},
{0x0000a400, 0x8fa91f01},
{0x0000a404, 0x00000000},
{0x0000a408, 0x0e79e5c6},
{0x0000a40c, 0x00820820},
{0x0000a414, 0x1ce42108},
{0x0000a418, 0x2d001dce},
{0x0000a41c, 0x1ce73908},
{0x0000a420, 0x000001ce},
{0x0000a424, 0x1ce738e7},
{0x0000a428, 0x000001ce},
{0x0000a42c, 0x1ce739ce},
{0x0000a430, 0x1ce739ce},
{0x0000a434, 0x00000000},
{0x0000a438, 0x00001801},
{0x0000a43c, 0x00100000},
{0x0000a444, 0x00000000},
{0x0000a448, 0x05000080},
{0x0000a44c, 0x00000001},
{0x0000a450, 0x00010000},
{0x0000a458, 0x00000000},
{0x0000a644, 0xbfad9d74},
{0x0000a648, 0x0048060a},
{0x0000a64c, 0x00003c37},
{0x0000a670, 0x03020100},
{0x0000a674, 0x09080504},
{0x0000a678, 0x0d0c0b0a},
{0x0000a67c, 0x13121110},
{0x0000a680, 0x31301514},
{0x0000a684, 0x35343332},
{0x0000a688, 0x00000036},
{0x0000a690, 0x08000838},
{0x0000a7cc, 0x00000000},
{0x0000a7d0, 0x00000000},
{0x0000a7d4, 0x00000004},
{0x0000a7dc, 0x00000000},
{0x0000a8d0, 0x004b6a8e},
{0x0000a8d4, 0x00000820},
{0x0000a8dc, 0x00000000},
{0x0000a8f0, 0x00000000},
{0x0000a8f4, 0x00000000},
{0x0000b2d0, 0x00000080},
{0x0000b2d4, 0x00000000},
{0x0000b2ec, 0x00000000},
{0x0000b2f0, 0x00000000},
{0x0000b2f4, 0x00000000},
{0x0000b2f8, 0x00000000},
{0x0000b408, 0x0e79e5c0},
{0x0000b40c, 0x00820820},
{0x0000b420, 0x00000000},
};
static const u32 qca953x_2p0_baseband_postamble[][5] = {
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
{0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
{0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
{0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
{0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
{0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
{0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
{0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
{0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
{0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
{0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
{0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
{0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
{0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0},
{0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
{0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
{0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
{0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
{0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
{0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
{0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
{0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
{0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
{0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
{0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
{0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
{0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
{0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
{0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
{0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
};
#endif /* INITVALS_953X_H */

View file

@ -22,6 +22,7 @@
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/completion.h>
#include <linux/time.h>
#include "common.h"
#include "debug.h"
@ -35,10 +36,7 @@ extern struct ieee80211_ops ath9k_ops;
extern int ath9k_modparam_nohwcrypt;
extern int led_blink;
extern bool is_ath9k_unloaded;
struct ath_config {
u16 txpowlimit;
};
extern int ath9k_use_chanctx;
/*************************/
/* Descriptor Management */
@ -167,7 +165,6 @@ struct ath_txq {
u32 axq_ampdu_depth;
bool stopped;
bool axq_tx_inprogress;
struct list_head axq_acq;
struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
u8 txq_headidx;
u8 txq_tailidx;
@ -280,8 +277,9 @@ struct ath_node {
struct ath_tx_control {
struct ath_txq *txq;
struct ath_node *an;
u8 paprd;
struct ieee80211_sta *sta;
u8 paprd;
bool force_channel;
};
@ -325,6 +323,116 @@ struct ath_rx {
u32 ampdu_ref;
};
struct ath_chanctx {
struct cfg80211_chan_def chandef;
struct list_head vifs;
struct list_head acq[IEEE80211_NUM_ACS];
int hw_queue_base;
/* do not dereference, use for comparison only */
struct ieee80211_vif *primary_sta;
struct ath_beacon_config beacon;
struct ath9k_hw_cal_data caldata;
struct timespec tsf_ts;
u64 tsf_val;
u32 last_beacon;
u16 txpower;
bool offchannel;
bool stopped;
bool active;
bool assigned;
bool switch_after_beacon;
};
enum ath_chanctx_event {
ATH_CHANCTX_EVENT_BEACON_PREPARE,
ATH_CHANCTX_EVENT_BEACON_SENT,
ATH_CHANCTX_EVENT_TSF_TIMER,
ATH_CHANCTX_EVENT_BEACON_RECEIVED,
ATH_CHANCTX_EVENT_ASSOC,
ATH_CHANCTX_EVENT_SWITCH,
ATH_CHANCTX_EVENT_UNASSIGN,
ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL,
};
enum ath_chanctx_state {
ATH_CHANCTX_STATE_IDLE,
ATH_CHANCTX_STATE_WAIT_FOR_BEACON,
ATH_CHANCTX_STATE_WAIT_FOR_TIMER,
ATH_CHANCTX_STATE_SWITCH,
ATH_CHANCTX_STATE_FORCE_ACTIVE,
};
struct ath_chanctx_sched {
bool beacon_pending;
bool offchannel_pending;
enum ath_chanctx_state state;
u8 beacon_miss;
u32 next_tbtt;
u32 switch_start_time;
unsigned int offchannel_duration;
unsigned int channel_switch_time;
/* backup, in case the hardware timer fails */
struct timer_list timer;
};
enum ath_offchannel_state {
ATH_OFFCHANNEL_IDLE,
ATH_OFFCHANNEL_PROBE_SEND,
ATH_OFFCHANNEL_PROBE_WAIT,
ATH_OFFCHANNEL_SUSPEND,
ATH_OFFCHANNEL_ROC_START,
ATH_OFFCHANNEL_ROC_WAIT,
ATH_OFFCHANNEL_ROC_DONE,
};
struct ath_offchannel {
struct ath_chanctx chan;
struct timer_list timer;
struct cfg80211_scan_request *scan_req;
struct ieee80211_vif *scan_vif;
int scan_idx;
enum ath_offchannel_state state;
struct ieee80211_channel *roc_chan;
struct ieee80211_vif *roc_vif;
int roc_duration;
int duration;
};
#define ath_for_each_chanctx(_sc, _ctx) \
for (ctx = &sc->chanctx[0]; \
ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \
ctx++)
void ath9k_fill_chanctx_ops(void);
void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
static inline struct ath_chanctx *
ath_chanctx_get(struct ieee80211_chanctx_conf *ctx)
{
struct ath_chanctx **ptr = (void *) ctx->drv_priv;
return *ptr;
}
void ath_chanctx_init(struct ath_softc *sc);
void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
void ath_offchannel_timer(unsigned long data);
void ath_offchannel_channel_change(struct ath_softc *sc);
void ath_chanctx_offchan_switch(struct ath_softc *sc,
struct ieee80211_channel *chan);
struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
bool active);
void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
enum ath_chanctx_event ev);
void ath_chanctx_timer(unsigned long data);
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@ -341,6 +449,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq);
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
void ath_txq_schedule_all(struct ath_softc *sc);
int ath_tx_init(struct ath_softc *sc, int nbufs);
int ath_txq_update(struct ath_softc *sc, int qnum,
struct ath9k_tx_queue_info *q);
@ -370,32 +479,47 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
/********/
struct ath_vif {
struct list_head list;
struct ieee80211_vif *vif;
struct ath_node mcast_node;
int av_bslot;
bool primary_sta_vif;
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
struct ath_buf *av_bcbuf;
struct ath_chanctx *chanctx;
/* P2P Client */
struct ieee80211_noa_data noa;
/* P2P GO */
u8 noa_index;
u32 offchannel_start;
u32 offchannel_duration;
u32 periodic_noa_start;
u32 periodic_noa_duration;
};
struct ath9k_vif_iter_data {
u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */
u8 mask[ETH_ALEN]; /* bssid mask */
bool has_hw_macaddr;
u8 slottime;
bool beacons;
int naps; /* number of AP vifs */
int nmeshes; /* number of mesh vifs */
int nstations; /* number of station vifs */
int nwds; /* number of WDS vifs */
int nadhocs; /* number of adhoc vifs */
struct ieee80211_vif *primary_sta;
};
void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void ath9k_calculate_iter_data(struct ath_softc *sc,
struct ath_chanctx *ctx,
struct ath9k_vif_iter_data *iter_data);
void ath9k_calculate_summary_state(struct ath_softc *sc,
struct ath_chanctx *ctx);
/*******************/
/* Beacon Handling */
@ -458,6 +582,7 @@ void ath9k_csa_update(struct ath_softc *sc);
#define ATH_PAPRD_TIMEOUT 100 /* msecs */
#define ATH_PLL_WORK_INTERVAL 100
void ath_chanctx_work(struct work_struct *work);
void ath_tx_complete_poll_work(struct work_struct *work);
void ath_reset_work(struct work_struct *work);
bool ath_hw_check(struct ath_softc *sc);
@ -473,6 +598,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
void ath_ps_full_sleep(unsigned long data);
void ath9k_p2p_ps_timer(void *priv);
void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
/**********/
/* BTCOEX */
@ -702,6 +828,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
#define PS_BEACON_SYNC BIT(4)
#define PS_WAIT_FOR_ANI BIT(5)
#define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
@ -720,6 +848,7 @@ struct ath_softc {
struct mutex mutex;
struct work_struct paprd_work;
struct work_struct hw_reset_work;
struct work_struct chanctx_work;
struct completion paprd_complete;
wait_queue_head_t tx_wait;
@ -738,23 +867,27 @@ struct ath_softc {
short nvifs;
unsigned long ps_usecount;
struct ath_config config;
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
struct cfg80211_chan_def cur_chandef;
struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
struct ath_chanctx *cur_chan;
struct ath_chanctx *next_chan;
spinlock_t chan_lock;
struct ath_offchannel offchannel;
struct ath_chanctx_sched sched;
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
char led_name[32];
struct led_classdev led_cdev;
#endif
struct ath9k_hw_cal_data caldata;
#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
#endif
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
struct delayed_work hw_pll_work;
struct timer_list sleep_timer;

View file

@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
u8 chainmask = ah->txchainmask;
u8 rate = 0;
sband = &common->sbands[common->hw->conf.chandef.chan->band];
sband = &common->sbands[sc->cur_chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
rate |= sband->bitrates[rateidx].hw_value_short;
@ -108,6 +108,55 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
}
static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
struct sk_buff *skb)
{
static const u8 noa_ie_hdr[] = {
WLAN_EID_VENDOR_SPECIFIC, /* type */
0, /* length */
0x50, 0x6f, 0x9a, /* WFA OUI */
0x09, /* P2P subtype */
0x0c, /* Notice of Absence */
0x00, /* LSB of little-endian len */
0x00, /* MSB of little-endian len */
};
struct ieee80211_p2p_noa_attr *noa;
int noa_len, noa_desc, i = 0;
u8 *hdr;
if (!avp->offchannel_duration && !avp->periodic_noa_duration)
return;
noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
hdr = skb_put(skb, sizeof(noa_ie_hdr));
memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr));
hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2;
hdr[7] = noa_len;
noa = (void *) skb_put(skb, noa_len);
memset(noa, 0, noa_len);
noa->index = avp->noa_index;
if (avp->periodic_noa_duration) {
u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
noa->desc[i].count = 255;
noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start);
noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration);
noa->desc[i].interval = cpu_to_le32(interval);
i++;
}
if (avp->offchannel_duration) {
noa->desc[i].count = 1;
noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start);
noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration);
}
}
static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@ -155,6 +204,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
if (vif->p2p)
ath9k_beacon_add_noa(sc, avp, skb);
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
skb->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
@ -249,7 +301,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
static int ath9k_beacon_choose_slot(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
u16 intval;
u32 tsftu;
u64 tsf;
@ -277,8 +329,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
u32 tsfadjust;
if (avp->av_bslot == 0)
@ -374,12 +426,19 @@ void ath9k_beacon_tasklet(unsigned long data)
vif = sc->beacon.bslot[slot];
/* EDMA devices check that in the tx completion function. */
if (!edma && ath9k_csa_is_finished(sc, vif))
return;
if (!edma) {
if (sc->sched.beacon_pending)
ath_chanctx_event(sc, NULL,
ATH_CHANCTX_EVENT_BEACON_SENT);
if (ath9k_csa_is_finished(sc, vif))
return;
}
if (!vif || !vif->bss_conf.enable_beacon)
return;
ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
bf = ath9k_beacon_generate(sc->hw, vif);
if (sc->beacon.bmisscnt != 0) {
@ -500,7 +559,6 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
struct ieee80211_vif *vif)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv;
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
if ((vif->type != NL80211_IFTYPE_AP) ||
@ -514,7 +572,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
if ((vif->type == NL80211_IFTYPE_STATION) &&
test_bit(ATH_OP_BEACONS, &common->op_flags) &&
!avp->primary_sta_vif) {
vif != sc->cur_chan->primary_sta) {
ath_dbg(common, CONFIG,
"Beacon already configured for a station interface\n");
return false;
@ -525,10 +583,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
}
static void ath9k_cache_beacon_config(struct ath_softc *sc,
struct ath_chanctx *ctx,
struct ieee80211_bss_conf *bss_conf)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_beacon_config *cur_conf = &ctx->beacon;
ath_dbg(common, BEACON,
"Caching beacon data for BSS: %pM\n", bss_conf->bssid);
@ -564,20 +623,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
u32 changed)
{
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_chanctx *ctx = avp->chanctx;
struct ath_beacon_config *cur_conf;
unsigned long flags;
bool skip_beacon = false;
if (!ctx)
return;
cur_conf = &avp->chanctx->beacon;
if (vif->type == NL80211_IFTYPE_AP)
ath9k_set_tsfadjust(sc, vif);
if (!ath9k_allow_beacon_config(sc, vif))
return;
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
ath9k_cache_beacon_config(sc, bss_conf);
if (vif->type == NL80211_IFTYPE_STATION) {
ath9k_cache_beacon_config(sc, ctx, bss_conf);
if (ctx != sc->cur_chan)
return;
ath9k_set_beacon(sc);
set_bit(ATH_OP_BEACONS, &common->op_flags);
return;
@ -593,10 +661,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
cur_conf->enable_beacon = false;
} else if (bss_conf->enable_beacon) {
cur_conf->enable_beacon = true;
ath9k_cache_beacon_config(sc, bss_conf);
ath9k_cache_beacon_config(sc, ctx, bss_conf);
}
}
if (ctx != sc->cur_chan)
return;
/*
* Configure the HW beacon registers only when we have a valid
* beacon interval.
@ -631,7 +702,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
void ath9k_set_beacon(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:

View file

@ -0,0 +1,685 @@
/*
* Copyright (c) 2014 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ath9k.h"
/* Set/change channels. If the channel is really being changed, it's done
* by reseting the chip. To accomplish this we must first cleanup any pending
* DMA, then restart stuff.
*/
static int ath_set_channel(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hw *hw = sc->hw;
struct ath9k_channel *hchan;
struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef;
struct ieee80211_channel *chan = chandef->chan;
int pos = chan->hw_value;
int old_pos = -1;
int r;
if (test_bit(ATH_OP_INVALID, &common->op_flags))
return -EIO;
if (ah->curchan)
old_pos = ah->curchan - &ah->channels[0];
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
chan->center_freq, chandef->width);
/* update survey stats for the old channel before switching */
spin_lock_bh(&common->cc_lock);
ath_update_survey_stats(sc);
spin_unlock_bh(&common->cc_lock);
ath9k_cmn_get_channel(hw, ah, chandef);
/* If the operating channel changes, change the survey in-use flags
* along with it.
* Reset the survey data for the new channel, unless we're switching
* back to the operating channel from an off-channel operation.
*/
if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) {
if (sc->cur_survey)
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
sc->cur_survey = &sc->survey[pos];
memset(sc->cur_survey, 0, sizeof(struct survey_info));
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
}
hchan = &sc->sc_ah->channels[pos];
r = ath_reset_internal(sc, hchan);
if (r)
return r;
/* The most recent snapshot of channel->noisefloor for the old
* channel is only available after the hardware reset. Copy it to
* the survey stats now.
*/
if (old_pos >= 0)
ath_update_survey_nf(sc, old_pos);
/* Enable radar pulse detection if on a DFS channel. Spectral
* scanning and radar detection can not be used concurrently.
*/
if (hw->conf.radar_enabled) {
u32 rxfilter;
/* set HW specific DFS configuration */
ath9k_hw_set_radar_params(ah);
rxfilter = ath9k_hw_getrxfilter(ah);
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
ATH9K_RX_FILTER_PHYERR;
ath9k_hw_setrxfilter(ah, rxfilter);
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
chan->center_freq);
} else {
/* perform spectral scan if requested. */
if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
return 0;
}
static bool
ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
bool powersave)
{
struct ieee80211_vif *vif = avp->vif;
struct ieee80211_sta *sta = NULL;
struct ieee80211_hdr_3addr *nullfunc;
struct ath_tx_control txctl;
struct sk_buff *skb;
int band = sc->cur_chan->chandef.chan->band;
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (!vif->bss_conf.assoc)
return false;
skb = ieee80211_nullfunc_get(sc->hw, vif);
if (!skb)
return false;
nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
if (powersave)
nullfunc->frame_control |=
cpu_to_le16(IEEE80211_FCTL_PM);
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
dev_kfree_skb_any(skb);
return false;
}
break;
default:
return false;
}
memset(&txctl, 0, sizeof(txctl));
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
txctl.sta = sta;
txctl.force_channel = true;
if (ath_tx_start(sc->hw, skb, &txctl)) {
ieee80211_free_txskb(sc->hw, skb);
return false;
}
return true;
}
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp;
bool active = false;
u8 n_active = 0;
if (!ctx)
return;
list_for_each_entry(avp, &ctx->vifs, list) {
struct ieee80211_vif *vif = avp->vif;
switch (vif->type) {
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
if (vif->bss_conf.assoc)
active = true;
break;
default:
active = true;
break;
}
}
ctx->active = active;
ath_for_each_chanctx(sc, ctx) {
if (!ctx->assigned || list_empty(&ctx->vifs))
continue;
n_active++;
}
if (n_active <= 1) {
clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
return;
}
if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
return;
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
}
static bool
ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave)
{
struct ath_vif *avp;
bool sent = false;
rcu_read_lock();
list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave))
sent = true;
}
rcu_read_unlock();
return sent;
}
static bool ath_chanctx_defer_switch(struct ath_softc *sc)
{
if (sc->cur_chan == &sc->offchannel.chan)
return false;
switch (sc->sched.state) {
case ATH_CHANCTX_STATE_SWITCH:
return false;
case ATH_CHANCTX_STATE_IDLE:
if (!sc->cur_chan->switch_after_beacon)
return false;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
break;
default:
break;
}
return true;
}
static void ath_chanctx_set_next(struct ath_softc *sc, bool force)
{
struct timespec ts;
bool measure_time = false;
bool send_ps = false;
spin_lock_bh(&sc->chan_lock);
if (!sc->next_chan) {
spin_unlock_bh(&sc->chan_lock);
return;
}
if (!force && ath_chanctx_defer_switch(sc)) {
spin_unlock_bh(&sc->chan_lock);
return;
}
if (sc->cur_chan != sc->next_chan) {
sc->cur_chan->stopped = true;
spin_unlock_bh(&sc->chan_lock);
if (sc->next_chan == &sc->offchannel.chan) {
getrawmonotonic(&ts);
measure_time = true;
}
__ath9k_flush(sc->hw, ~0, true);
if (ath_chanctx_send_ps_frame(sc, true))
__ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
send_ps = true;
spin_lock_bh(&sc->chan_lock);
if (sc->cur_chan != &sc->offchannel.chan) {
getrawmonotonic(&sc->cur_chan->tsf_ts);
sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
}
}
sc->cur_chan = sc->next_chan;
sc->cur_chan->stopped = false;
sc->next_chan = NULL;
sc->sched.offchannel_duration = 0;
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
spin_unlock_bh(&sc->chan_lock);
if (sc->sc_ah->chip_fullsleep ||
memcmp(&sc->cur_chandef, &sc->cur_chan->chandef,
sizeof(sc->cur_chandef))) {
ath_set_channel(sc);
if (measure_time)
sc->sched.channel_switch_time =
ath9k_hw_get_tsf_offset(&ts, NULL);
}
if (send_ps)
ath_chanctx_send_ps_frame(sc, false);
ath_offchannel_channel_change(sc);
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH);
}
void ath_chanctx_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc,
chanctx_work);
mutex_lock(&sc->mutex);
ath_chanctx_set_next(sc, false);
mutex_unlock(&sc->mutex);
}
void ath_chanctx_init(struct ath_softc *sc)
{
struct ath_chanctx *ctx;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
int i, j;
sband = &common->sbands[IEEE80211_BAND_2GHZ];
if (!sband->n_channels)
sband = &common->sbands[IEEE80211_BAND_5GHZ];
chan = &sband->channels[0];
for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
ctx = &sc->chanctx[i];
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
ctx->txpower = ATH_TXPOWER_MAX;
for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
INIT_LIST_HEAD(&ctx->acq[j]);
}
ctx = &sc->offchannel.chan;
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
ctx->txpower = ATH_TXPOWER_MAX;
for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
INIT_LIST_HEAD(&ctx->acq[j]);
sc->offchannel.chan.offchannel = true;
}
void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
bool changed = false;
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
return;
if (!avp->chanctx)
return;
mutex_lock(&sc->mutex);
spin_lock_bh(&sc->chan_lock);
if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
sc->next_chan = avp->chanctx;
changed = true;
}
sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
spin_unlock_bh(&sc->chan_lock);
if (changed)
ath_chanctx_set_next(sc, true);
mutex_unlock(&sc->mutex);
}
void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
spin_lock_bh(&sc->chan_lock);
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
(sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
sc->sched.offchannel_pending = true;
spin_unlock_bh(&sc->chan_lock);
return;
}
sc->next_chan = ctx;
if (chandef)
ctx->chandef = *chandef;
if (sc->next_chan == &sc->offchannel.chan) {
sc->sched.offchannel_duration =
TU_TO_USEC(sc->offchannel.duration) +
sc->sched.channel_switch_time;
}
spin_unlock_bh(&sc->chan_lock);
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
}
void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef)
{
bool cur_chan;
spin_lock_bh(&sc->chan_lock);
if (chandef)
memcpy(&ctx->chandef, chandef, sizeof(*chandef));
cur_chan = sc->cur_chan == ctx;
spin_unlock_bh(&sc->chan_lock);
if (!cur_chan)
return;
ath_set_channel(sc);
}
struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active)
{
struct ath_chanctx *ctx;
ath_for_each_chanctx(sc, ctx) {
if (!ctx->assigned || list_empty(&ctx->vifs))
continue;
if (active && !ctx->active)
continue;
if (ctx->switch_after_beacon)
return ctx;
}
return &sc->chanctx[0];
}
void ath_chanctx_offchan_switch(struct ath_softc *sc,
struct ieee80211_channel *chan)
{
struct cfg80211_chan_def chandef;
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
}
static struct ath_chanctx *
ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx)
{
int idx = ctx - &sc->chanctx[0];
return &sc->chanctx[!idx];
}
static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
{
struct ath_chanctx *prev, *cur;
struct timespec ts;
u32 cur_tsf, prev_tsf, beacon_int;
s32 offset;
beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
cur = sc->cur_chan;
prev = ath_chanctx_get_next(sc, cur);
getrawmonotonic(&ts);
cur_tsf = (u32) cur->tsf_val +
ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf;
prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts);
/* Adjust the TSF time of the AP chanctx to keep its beacons
* at half beacon interval offset relative to the STA chanctx.
*/
offset = cur_tsf - prev_tsf;
/* Ignore stale data or spurious timestamps */
if (offset < 0 || offset > 3 * beacon_int)
return;
offset = beacon_int / 2 - (offset % beacon_int);
prev->tsf_val += offset;
}
void ath_chanctx_timer(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *) data;
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
}
/* Configure the TSF based hardware timer for a channel switch.
* Also set up backup software timer, in case the gen timer fails.
* This could be caused by a hardware reset.
*/
static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
{
struct ath_hw *ah = sc->sc_ah;
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
tsf_time -= ath9k_hw_gettsf32(ah);
tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
mod_timer(&sc->sched.timer, tsf_time);
}
void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
enum ath_chanctx_event ev)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_beacon_config *cur_conf;
struct ath_vif *avp = NULL;
struct ath_chanctx *ctx;
u32 tsf_time;
u32 beacon_int;
bool noa_changed = false;
if (vif)
avp = (struct ath_vif *) vif->drv_priv;
spin_lock_bh(&sc->chan_lock);
switch (ev) {
case ATH_CHANCTX_EVENT_BEACON_PREPARE:
if (avp->offchannel_duration)
avp->offchannel_duration = 0;
if (avp->chanctx != sc->cur_chan)
break;
if (sc->sched.offchannel_pending) {
sc->sched.offchannel_pending = false;
sc->next_chan = &sc->offchannel.chan;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
}
ctx = ath_chanctx_get_next(sc, sc->cur_chan);
if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) {
sc->next_chan = ctx;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
}
/* if the timer missed its window, use the next interval */
if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
sc->sched.beacon_pending = true;
sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
cur_conf = &sc->cur_chan->beacon;
beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
/* defer channel switch by a quarter beacon interval */
tsf_time = sc->sched.next_tbtt + beacon_int / 4;
sc->sched.switch_start_time = tsf_time;
sc->cur_chan->last_beacon = sc->sched.next_tbtt;
/* Prevent wrap-around issues */
if (avp->periodic_noa_duration &&
tsf_time - avp->periodic_noa_start > BIT(30))
avp->periodic_noa_duration = 0;
if (ctx->active && !avp->periodic_noa_duration) {
avp->periodic_noa_start = tsf_time;
avp->periodic_noa_duration =
TU_TO_USEC(cur_conf->beacon_interval) / 2 -
sc->sched.channel_switch_time;
noa_changed = true;
} else if (!ctx->active && avp->periodic_noa_duration) {
avp->periodic_noa_duration = 0;
noa_changed = true;
}
/* If at least two consecutive beacons were missed on the STA
* chanctx, stay on the STA channel for one extra beacon period,
* to resync the timer properly.
*/
if (ctx->active && sc->sched.beacon_miss >= 2)
sc->sched.offchannel_duration = 3 * beacon_int / 2;
if (sc->sched.offchannel_duration) {
noa_changed = true;
avp->offchannel_start = tsf_time;
avp->offchannel_duration =
sc->sched.offchannel_duration;
}
if (noa_changed)
avp->noa_index++;
break;
case ATH_CHANCTX_EVENT_BEACON_SENT:
if (!sc->sched.beacon_pending)
break;
sc->sched.beacon_pending = false;
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
break;
case ATH_CHANCTX_EVENT_TSF_TIMER:
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
break;
if (!sc->cur_chan->switch_after_beacon &&
sc->sched.beacon_pending)
sc->sched.beacon_miss++;
sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
case ATH_CHANCTX_EVENT_BEACON_RECEIVED:
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
sc->cur_chan == &sc->offchannel.chan)
break;
ath_chanctx_adjust_tbtt_delta(sc);
sc->sched.beacon_pending = false;
sc->sched.beacon_miss = 0;
/* TSF time might have been updated by the incoming beacon,
* need update the channel switch timer to reflect the change.
*/
tsf_time = sc->sched.switch_start_time;
tsf_time -= (u32) sc->cur_chan->tsf_val +
ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
tsf_time += ath9k_hw_gettsf32(ah);
ath_chanctx_setup_timer(sc, tsf_time);
break;
case ATH_CHANCTX_EVENT_ASSOC:
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
avp->chanctx != sc->cur_chan)
break;
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
/* fall through */
case ATH_CHANCTX_EVENT_SWITCH:
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
sc->cur_chan->switch_after_beacon ||
sc->cur_chan == &sc->offchannel.chan)
break;
/* If this is a station chanctx, stay active for a half
* beacon period (minus channel switch time)
*/
sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
cur_conf = &sc->cur_chan->beacon;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
if (sc->sched.beacon_miss >= 2) {
sc->sched.beacon_miss = 0;
tsf_time *= 3;
}
tsf_time -= sc->sched.channel_switch_time;
tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
sc->sched.switch_start_time = tsf_time;
ath_chanctx_setup_timer(sc, tsf_time);
sc->sched.beacon_pending = true;
break;
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
if (sc->cur_chan == &sc->offchannel.chan ||
sc->cur_chan->switch_after_beacon)
break;
sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
case ATH_CHANCTX_EVENT_UNASSIGN:
if (sc->cur_chan->assigned) {
if (sc->next_chan && !sc->next_chan->assigned &&
sc->next_chan != &sc->offchannel.chan)
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
break;
}
ctx = ath_chanctx_get_next(sc, sc->cur_chan);
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
if (!ctx->assigned)
break;
sc->next_chan = ctx;
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
}
spin_unlock_bh(&sc->chan_lock);
}

View file

@ -57,7 +57,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
struct ath9k_beacon_state *bs)
{
struct ath_common *common = ath9k_hw_common(ah);
int dtim_intval;
int dtim_intval, sleepduration;
u64 tsf;
/* No need to configure beacon if we are not associated */
@ -75,6 +75,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
* last beacon we received (which may be none).
*/
dtim_intval = conf->intval * conf->dtim_period;
sleepduration = ah->hw->conf.listen_interval * conf->intval;
/*
* Pull nexttbtt forward to reflect the current
@ -112,7 +113,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
*/
bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
conf->intval));
sleepduration));
if (bs->bs_sleepduration > bs->bs_dtimperiod)
bs->bs_sleepduration = bs->bs_dtimperiod;

View file

@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ieee80211_hw *hw = sc->hw;
struct ath9k_vif_iter_data iter_data;
struct ath_chanctx *ctx;
char buf[512];
unsigned int len = 0;
ssize_t retval = 0;
unsigned int reg;
u32 rxfilter;
u32 rxfilter, i;
len += scnprintf(buf + len, sizeof(buf) - len,
"BSSID: %pM\n", common->curbssid);
@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
len += scnprintf(buf + len, sizeof(buf) - len, "\n");
ath9k_calculate_iter_data(hw, NULL, &iter_data);
i = 0;
ath_for_each_chanctx(sc, ctx) {
if (!ctx->assigned || list_empty(&ctx->vifs))
continue;
ath9k_calculate_iter_data(sc, ctx, &iter_data);
len += scnprintf(buf + len, sizeof(buf) - len,
"VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
" ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
iter_data.naps, iter_data.nstations, iter_data.nmeshes,
iter_data.nwds, iter_data.nadhocs,
sc->nvifs, sc->nbcnvifs);
len += scnprintf(buf + len, sizeof(buf) - len,
"VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
i++, iter_data.naps, iter_data.nstations,
iter_data.nmeshes, iter_data.nwds);
len += scnprintf(buf + len, sizeof(buf) - len,
" ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
iter_data.nadhocs, sc->nvifs, sc->nbcnvifs);
}
if (len > sizeof(buf))
len = sizeof(buf);
@ -1080,7 +1086,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
u32 len = 0, size = 1500;

View file

@ -791,7 +791,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
refdiv = 5;
} else {
pll2_divint = 0x11;
pll2_divfrac = 0x26666;
pll2_divfrac =
AR_SREV_9531(ah) ? 0x26665 : 0x26666;
refdiv = 1;
}
}
@ -1730,6 +1731,23 @@ fail:
return -EINVAL;
}
u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur)
{
struct timespec ts;
s64 usec;
if (!cur) {
getrawmonotonic(&ts);
cur = &ts;
}
usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000;
usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000;
return (u32) usec;
}
EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
struct ath9k_hw_cal_data *caldata, bool fastcc)
{
@ -1739,7 +1757,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
u32 saveDefAntenna;
u32 macStaId1;
u64 tsf = 0;
s64 usec = 0;
int r;
bool start_mci_reset = false;
bool save_fullsleep = ah->chip_fullsleep;
@ -1785,7 +1802,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
/* Save TSF before chip reset, a cold reset clears it */
tsf = ath9k_hw_gettsf64(ah);
getrawmonotonic(&ts);
usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
saveLedState = REG_READ(ah, AR_CFG_LED) &
(AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@ -1818,9 +1834,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
}
/* Restore TSF */
getrawmonotonic(&ts);
usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
ath9k_hw_settsf64(ah, tsf + usec);
ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL));
if (AR_SREV_9280_20_OR_LATER(ah))
REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);

View file

@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah);
u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
void ath9k_hw_init_global_settings(struct ath_hw *ah);
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);

View file

@ -61,7 +61,7 @@ static int ath9k_ps_enable;
module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
static int ath9k_use_chanctx;
int ath9k_use_chanctx;
module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
/* Set tx power */
if (ah->curchan) {
sc->config.txpowlimit = 2 * ah->curchan->chan->max_power;
sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
ath9k_ps_wakeup(sc);
ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
/* synchronize DFS detector if regulatory domain changed */
if (sc->dfs_detector != NULL)
@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->config.txpowlimit = ATH_TXPOWER_MAX;
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@ -511,6 +510,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
sc->tx99_power = MAX_RATE_POWER + 1;
init_waitqueue_head(&sc->tx_wait);
sc->cur_chan = &sc->chanctx[0];
if (!ath9k_use_chanctx)
sc->cur_chan->hw_queue_base = 0;
if (!pdata || pdata->use_eeprom) {
ah->ah_flags |= AH_USE_EEPROM;
@ -556,6 +558,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&common->cc_lock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
spin_lock_init(&sc->chan_lock);
mutex_init(&sc->mutex);
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
@ -564,7 +567,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
INIT_WORK(&sc->hw_reset_work, ath_reset_work);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
(unsigned long)sc);
setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc);
/*
* Cache line size is used to size and align various
@ -599,6 +606,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);
ath_fill_led_pin(sc);
ath_chanctx_init(sc);
if (common->bus_ops->aspm_init)
common->bus_ops->aspm_init(common);
@ -664,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = {
{ .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) },
};
static const struct ieee80211_iface_limit if_limits_multi[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_STATION) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) },
};
static const struct ieee80211_iface_limit if_dfs_limits[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
@ -672,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = {
BIT(NL80211_IFTYPE_ADHOC) },
};
static const struct ieee80211_iface_combination if_comb_multi[] = {
{
.limits = if_limits_multi,
.n_limits = ARRAY_SIZE(if_limits_multi),
.max_interfaces = 2,
.num_different_channels = 2,
.beacon_int_infra_match = true,
},
};
static const struct ieee80211_iface_combination if_comb[] = {
{
.limits = if_limits,
@ -712,6 +736,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_RC_TABLE |
IEEE80211_HW_QUEUE_CONTROL |
IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
if (ath9k_ps_enable)
@ -739,12 +764,21 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->iface_combinations = if_comb;
if (!ath9k_use_chanctx) {
hw->wiphy->iface_combinations = if_comb;
hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
} else
hw->wiphy->n_iface_combinations = 1;
} else {
hw->wiphy->iface_combinations = if_comb_multi;
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(if_comb_multi);
hw->wiphy->max_scan_ssids = 255;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
hw->wiphy->max_remain_on_channel_duration = 10000;
hw->chanctx_data_size = sizeof(void *);
hw->extra_beacon_tailroom =
sizeof(struct ieee80211_p2p_noa_attr) + 9;
}
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@ -756,9 +790,14 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->queues = 4;
/* allow 4 queues per channel context +
* 1 cab queue + 1 offchannel tx queue
*/
hw->queues = 10;
/* last queue for offchannel */
hw->offchannel_tx_hw_queue = hw->queues - 1;
hw->max_rates = 4;
hw->max_listen_interval = 1;
hw->max_listen_interval = 10;
hw->max_rate_tries = 10;
hw->sta_data_size = sizeof(struct ath_node);
hw->vif_data_size = sizeof(struct ath_vif);

View file

@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
memset(tx_info, 0, sizeof(*tx_info));
tx_info->band = hw->conf.chandef.chan->band;
tx_info->band = sc->cur_chandef.chan->band;
tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
tx_info->control.rates[0].idx = 0;
tx_info->control.rates[0].count = 1;
@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc)
if (common->disable_ani ||
!test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
sc->cur_chan->offchannel)
return;
common->ani.longcal_timer = timestamp;
@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
/*
* Check for the various conditions in which ANI has to

File diff suppressed because it is too large Load diff

View file

@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
return;
if (setchannel) {
struct ath9k_hw_cal_data *caldata = &sc->caldata;
struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
if (IS_CHAN_HT40PLUS(ah->curchan) &&
(ah->curchan->channel > caldata->channel) &&
(ah->curchan->channel <= caldata->channel + 20))
@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
mci_hw->concur_tx = concur_tx;
if (old_concur_tx != mci_hw->concur_tx)
ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
}
static void ath9k_mci_stomp_audio(struct ath_softc *sc)

View file

@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
ath9k_fill_chanctx_ops();
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
if (!hw) {
dev_err(&pdev->dev, "No memory for ieee80211_hw\n");

View file

@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
ath_opmode_init(sc);
ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
}
static void ath_edma_stop_recv(struct ath_softc *sc)
@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
u32 ath_calcrxfilter(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u32 rfilt;
if (config_enabled(CONFIG_ATH9K_TX99))
@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
if (ath9k_use_chanctx &&
test_bit(ATH_OP_SCANNING, &common->op_flags))
rfilt |= ATH9K_RX_FILTER_BEACON;
return rfilt;
}
@ -457,7 +462,7 @@ int ath_startrecv(struct ath_softc *sc)
start_recv:
ath_opmode_init(sc);
ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
return 0;
}
@ -540,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
sc->ps_flags &= ~PS_BEACON_SYNC;
ath_dbg(common, PS,
"Reconfigure beacon timers based on synchronized timestamp\n");
if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
ath9k_set_beacon(sc);
if (sc->p2p_ps_vif)
ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
@ -887,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
return -EINVAL;
}
if (rx_stats->is_mybeacon) {
sc->sched.next_tbtt = rx_stats->rs_tstamp;
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED);
}
ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
rx_status->band = ah->curchan->chan->band;

View file

@ -813,6 +813,7 @@
#define AR_SREV_VERSION_9531 0x500
#define AR_SREV_REVISION_9531_10 0
#define AR_SREV_REVISION_9531_11 1
#define AR_SREV_REVISION_9531_20 2
#define AR_SREV_5416(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
@ -958,6 +959,9 @@
#define AR_SREV_9531_11(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11))
#define AR_SREV_9531_20(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20))
/* NOTE: When adding chips newer than Peacock, add chip check here */
#define AR_SREV_9580_10_OR_LATER(_ah) \

View file

@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
tx_info = IEEE80211_SKB_CB(skb);
memset(tx_info, 0, sizeof(*tx_info));
rate = &tx_info->control.rates[0];
tx_info->band = hw->conf.chandef.chan->band;
tx_info->band = sc->cur_chan->chandef.chan->band;
tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
tx_info->control.vif = sc->tx99_vif;
rate->count = 1;

View file

@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
u32 wow_triggers_enabled = 0;
int ret = 0;
cancel_work_sync(&sc->chanctx_work);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);

View file

@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_tx_status(sc->hw, skb);
}
static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
struct ath_atx_tid *tid)
{
struct ath_atx_ac *ac = tid->ac;
struct list_head *list;
struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv;
struct ath_chanctx *ctx = avp->chanctx;
if (!ctx)
return;
if (tid->sched)
return;
@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
return;
ac->sched = true;
list_add_tail(&ac->list, &txq->axq_acq);
list = &ctx->acq[TID_TO_WME_AC(tid->tidno)];
list_add_tail(&ac->list, list);
}
static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
@ -147,7 +156,8 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
struct sk_buff *skb)
{
int q;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int q, hw_queue;
q = skb_get_queue_mapping(skb);
if (txq == sc->tx.uapsdq)
@ -159,9 +169,10 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
if (WARN_ON(--txq->pending_frames < 0))
txq->pending_frames = 0;
hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
if (txq->stopped &&
txq->pending_frames < sc->tx.txq_max_pending[q]) {
ieee80211_wake_queue(sc->hw, q);
ieee80211_wake_queue(sc->hw, hw_queue);
txq->stopped = false;
}
}
@ -626,7 +637,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
skb_queue_splice_tail(&bf_pending, &tid->retry_q);
if (!an->sleeping) {
ath_tx_queue_tid(txq, tid);
ath_tx_queue_tid(sc, txq, tid);
if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
tid->ac->clear_ps_filter = true;
@ -1483,7 +1494,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
ac->clear_ps_filter = true;
if (ath_tid_has_buffered(tid)) {
ath_tx_queue_tid(txq, tid);
ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@ -1507,7 +1518,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
if (ath_tid_has_buffered(tid)) {
ath_tx_queue_tid(txq, tid);
ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@ -1642,7 +1653,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->axq_link = NULL;
__skb_queue_head_init(&txq->complete_q);
INIT_LIST_HEAD(&txq->axq_q);
INIT_LIST_HEAD(&txq->axq_acq);
spin_lock_init(&txq->axq_lock);
txq->axq_depth = 0;
txq->axq_ampdu_depth = 0;
@ -1686,7 +1696,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
int ath_cabq_update(struct ath_softc *sc)
{
struct ath9k_tx_queue_info qi;
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
int qnum = sc->beacon.cabq->axq_qnum;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
@ -1804,7 +1814,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
}
/* For each axq_acq entry, for each tid, try to schedule packets
/* For each acq entry, for each tid, try to schedule packets
* for transmit until ampdu_depth has reached min Q depth.
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
@ -1812,19 +1822,31 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_ac *ac, *last_ac;
struct ath_atx_tid *tid, *last_tid;
struct list_head *ac_list;
bool sent = false;
if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
list_empty(&txq->axq_acq))
if (txq->mac80211_qnum < 0)
return;
spin_lock_bh(&sc->chan_lock);
ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
spin_unlock_bh(&sc->chan_lock);
if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
list_empty(ac_list))
return;
spin_lock_bh(&sc->chan_lock);
rcu_read_lock();
last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
while (!list_empty(&txq->axq_acq)) {
last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
while (!list_empty(ac_list)) {
bool stop = false;
ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
if (sc->cur_chan->stopped)
break;
ac = list_first_entry(ac_list, struct ath_atx_ac, list);
last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
list_del(&ac->list);
ac->sched = false;
@ -1844,7 +1866,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
* are pending for the tid
*/
if (ath_tid_has_buffered(tid))
ath_tx_queue_tid(txq, tid);
ath_tx_queue_tid(sc, txq, tid);
if (stop || tid == last_tid)
break;
@ -1852,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
if (!list_empty(&ac->tid_q) && !ac->sched) {
ac->sched = true;
list_add_tail(&ac->list, &txq->axq_acq);
list_add_tail(&ac->list, ac_list);
}
if (stop)
@ -1863,12 +1885,27 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
break;
sent = false;
last_ac = list_entry(txq->axq_acq.prev,
last_ac = list_entry(ac_list->prev,
struct ath_atx_ac, list);
}
}
rcu_read_unlock();
spin_unlock_bh(&sc->chan_lock);
}
void ath_txq_schedule_all(struct ath_softc *sc)
{
struct ath_txq *txq;
int i;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
txq = sc->tx.txq_map[i];
spin_lock_bh(&txq->axq_lock);
ath_txq_schedule(sc, txq);
spin_unlock_bh(&txq->axq_lock);
}
}
/***********/
@ -2150,13 +2187,21 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
struct ath_vif *avp = NULL;
struct ath_softc *sc = hw->priv;
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
int q;
bool queue;
int q, hw_queue;
int ret;
if (vif)
avp = (void *)vif->drv_priv;
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
txctl->force_channel = true;
ret = ath_tx_prepare(hw, skb, txctl);
if (ret)
return ret;
@ -2168,24 +2213,39 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
*/
q = skb_get_queue_mapping(skb);
hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
ath_txq_lock(sc, txq);
if (txq == sc->tx.txq_map[q] &&
++txq->pending_frames > sc->tx.txq_max_pending[q] &&
!txq->stopped) {
ieee80211_stop_queue(sc->hw, q);
ieee80211_stop_queue(sc->hw, hw_queue);
txq->stopped = true;
}
if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
queue = ieee80211_is_data_present(hdr->frame_control);
/* Force queueing of all frames that belong to a virtual interface on
* a different channel context, to ensure that they are sent on the
* correct channel.
*/
if (((avp && avp->chanctx != sc->cur_chan) ||
sc->cur_chan->stopped) && !txctl->force_channel) {
if (!txctl->an)
txctl->an = &avp->mcast_node;
info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE;
queue = true;
}
if (txctl->an && queue)
tid = ath_get_skb_tid(sc, txctl->an, skb);
if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE |
IEEE80211_TX_CTL_TX_OFFCHAN)) {
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
ath_txq_lock(sc, txq);
} else if (txctl->an &&
ieee80211_is_data_present(hdr->frame_control)) {
} else if (txctl->an && queue) {
WARN_ON(tid->ac->txq != txctl->txq);
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
@ -2198,7 +2258,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
TX_STAT_INC(txq->axq_qnum, a_queued_sw);
__skb_queue_tail(&tid->buf_q, skb);
if (!txctl->an->sleeping)
ath_tx_queue_tid(txq, tid);
ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
goto out;
@ -2244,8 +2304,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int max_duration;
max_duration =
sc->cur_beacon_conf.beacon_interval * 1000 *
sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
sc->cur_chan->beacon.beacon_interval * 1000 *
sc->cur_chan->beacon.dtim_period / ATH_BCBUF;
do {
struct ath_frame_info *fi = get_frame_info(skb);
@ -2560,6 +2620,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
sc->beacon.tx_processed = true;
sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
ath_chanctx_event(sc, NULL,
ATH_CHANCTX_EVENT_BEACON_SENT);
ath9k_csa_update(sc);
continue;
}

View file

@ -104,8 +104,8 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
return -EOPNOTSUPP;
}
static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
struct station_info *sinfo)
int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
struct station_info *sinfo)
{
struct wmi_notify_req_cmd cmd = {
.cid = cid,
@ -287,6 +287,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
wil->scan_request = request;
mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
@ -443,15 +444,15 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
return rc;
}
static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie)
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie)
{
const u8 *buf = params->buf;
size_t len = params->len;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
bool tx_status = false;
struct ieee80211_mgmt *mgmt_frame = (void *)buf;
struct wmi_sw_tx_req_cmd *cmd;
struct {
@ -460,8 +461,10 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
} __packed evt;
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
if (!cmd) {
rc = -ENOMEM;
goto out;
}
memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
cmd->len = cpu_to_le16(len);
@ -470,10 +473,12 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
rc = evt.evt.status;
tx_status = !evt.evt.status;
kfree(cmd);
out:
cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
tx_status, GFP_KERNEL);
return rc;
}
@ -562,6 +567,34 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
return rc;
}
static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
{
print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET,
b->head, b->head_len);
print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET,
b->tail, b->tail_len);
print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET,
b->beacon_ies, b->beacon_ies_len);
print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET,
b->probe_resp, b->probe_resp_len);
print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET,
b->proberesp_ies, b->proberesp_ies_len);
print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET,
b->assocresp_ies, b->assocresp_ies_len);
}
static void wil_print_crypto(struct wil6210_priv *wil,
struct cfg80211_crypto_settings *c)
{
wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
c->wpa_versions, c->cipher_group);
wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
c->control_port, be16_to_cpu(c->control_port_ethertype),
c->control_port_no_encrypt);
}
static int wil_fix_bcon(struct wil6210_priv *wil,
struct cfg80211_beacon_data *bcon)
{
@ -595,8 +628,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct ieee80211_channel *channel = info->chandef.chan;
struct cfg80211_beacon_data *bcon = &info->beacon;
struct cfg80211_crypto_settings *crypto = &info->crypto;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
wil_dbg_misc(wil, "%s()\n", __func__);
if (!channel) {
wil_err(wil, "AP: No channel???\n");
return -EINVAL;
@ -604,11 +640,19 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
channel->center_freq, info->privacy ? "secure" : "open");
wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
info->privacy, info->auth_type);
wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
info->dtim_period);
print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
info->ssid, info->ssid_len);
wil_print_bcon_data(bcon);
wil_print_crypto(wil, crypto);
if (wil_fix_bcon(wil, bcon))
if (wil_fix_bcon(wil, bcon)) {
wil_dbg_misc(wil, "Fixed bcon\n");
wil_print_bcon_data(bcon);
}
mutex_lock(&wil->mutex);
@ -663,6 +707,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->mutex);
rc = wmi_pcp_stop(wil);

View file

@ -19,6 +19,7 @@
#include <linux/seq_file.h>
#include <linux/pci.h>
#include <linux/rtnetlink.h>
#include <linux/power_supply.h>
#include "wil6210.h"
#include "txrx.h"
@ -69,14 +70,32 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
if (vring->va) {
int cid = wil->vring2cid_tid[i][0];
int tid = wil->vring2cid_tid[i][1];
u32 swhead = vring->swhead;
u32 swtail = vring->swtail;
int used = (vring->size + swhead - swtail)
% vring->size;
int avail = vring->size - used - 1;
char name[10];
/* performance monitoring */
cycles_t now = get_cycles();
cycles_t idle = txdata->idle * 100;
cycles_t total = now - txdata->begin;
do_div(idle, total);
txdata->begin = now;
txdata->idle = 0ULL;
snprintf(name, sizeof(name), "tx_%2d", i);
seq_printf(s, "\n%pM CID %d TID %d\n",
wil->sta[cid].addr, cid, tid);
seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n",
wil->sta[cid].addr, cid, tid, used, avail,
(int)idle);
wil_print_vring(s, wil, name, vring, '_', 'H');
}
}
@ -231,6 +250,26 @@ static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
&fops_iomem_x32);
}
static int wil_debugfs_ulong_set(void *data, u64 val)
{
*(ulong *)data = val;
return 0;
}
static int wil_debugfs_ulong_get(void *data, u64 *val)
{
*val = *(ulong *)data;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
wil_debugfs_ulong_set, "%llu\n");
static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode,
struct dentry *parent,
ulong *value)
{
return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong);
}
static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
const char *name,
struct dentry *parent, u32 off)
@ -284,11 +323,11 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
if (IS_ERR_OR_NULL(d))
return -ENODEV;
wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_DATA));
wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_CRL));
return 0;
@ -397,6 +436,124 @@ static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
.open = simple_open,
};
/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
int rc;
long channel;
bool on;
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
if (copy_from_user(kbuf, buf, len))
return -EIO;
kbuf[len] = '\0';
rc = kstrtol(kbuf, 0, &channel);
kfree(kbuf);
if (rc)
return rc;
if ((channel < 0) || (channel > 4)) {
wil_err(wil, "Invalid channel %ld\n", channel);
return -EINVAL;
}
on = !!channel;
if (on) {
rc = wmi_set_channel(wil, (int)channel);
if (rc)
return rc;
}
rc = wmi_rxon(wil, on);
if (rc)
return rc;
return len;
}
static const struct file_operations fops_rxon = {
.write = wil_write_file_rxon,
.open = simple_open,
};
/*---tx_mgmt---*/
/* Write mgmt frame to this file to send it */
static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct wiphy *wiphy = wil_to_wiphy(wil);
struct wireless_dev *wdev = wil_to_wdev(wil);
struct cfg80211_mgmt_tx_params params;
int rc;
void *frame = kmalloc(len, GFP_KERNEL);
if (!frame)
return -ENOMEM;
if (copy_from_user(frame, buf, len))
return -EIO;
params.buf = frame;
params.len = len;
params.chan = wdev->preset_chandef.chan;
rc = wil_cfg80211_mgmt_tx(wiphy, wdev, &params, NULL);
kfree(frame);
wil_info(wil, "%s() -> %d\n", __func__, rc);
return len;
}
static const struct file_operations fops_txmgmt = {
.write = wil_write_file_txmgmt,
.open = simple_open,
};
/* Write WMI command (w/o mbox header) to this file to send it
* WMI starts from wil6210_mbox_hdr_wmi header
*/
static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct wil6210_mbox_hdr_wmi *wmi;
void *cmd;
int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi);
u16 cmdid;
int rc, rc1;
if (cmdlen <= 0)
return -EINVAL;
wmi = kmalloc(len, GFP_KERNEL);
if (!wmi)
return -ENOMEM;
rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
if (rc < 0)
return rc;
cmd = &wmi[1];
cmdid = le16_to_cpu(wmi->id);
rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
kfree(wmi);
wil_info(wil, "%s(0x%04x[%d]) -> %d\n", __func__, cmdid, cmdlen, rc1);
return rc;
}
static const struct file_operations fops_wmi = {
.write = wil_write_file_wmi,
.open = simple_open,
};
static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
const char *prefix)
@ -600,8 +757,8 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
return 0;
}
print_temp(s, "MAC temperature :", t_m);
print_temp(s, "Radio temperature :", t_r);
print_temp(s, "T_mac =", t_m);
print_temp(s, "T_radio =", t_r);
return 0;
}
@ -618,6 +775,130 @@ static const struct file_operations fops_temp = {
.llseek = seq_lseek,
};
/*---------freq------------*/
static int wil_freq_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
struct wireless_dev *wdev = wil_to_wdev(wil);
u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
seq_printf(s, "Freq = %d\n", freq);
return 0;
}
static int wil_freq_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_freq_debugfs_show, inode->i_private);
}
static const struct file_operations fops_freq = {
.open = wil_freq_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/*---------link------------*/
static int wil_link_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
struct station_info sinfo;
int i, rc;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
struct wil_sta_info *p = &wil->sta[i];
char *status = "unknown";
switch (p->status) {
case wil_sta_unused:
status = "unused ";
break;
case wil_sta_conn_pending:
status = "pending ";
break;
case wil_sta_connected:
status = "connected";
break;
}
seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
(p->data_port_open ? " data_port_open" : ""));
if (p->status == wil_sta_connected) {
rc = wil_cid_fill_sinfo(wil, i, &sinfo);
if (rc)
return rc;
seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs);
seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs);
seq_printf(s, " SQ = %d\n", sinfo.signal);
}
}
return 0;
}
static int wil_link_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_link_debugfs_show, inode->i_private);
}
static const struct file_operations fops_link = {
.open = wil_link_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/*---------info------------*/
static int wil_info_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
struct net_device *ndev = wil_to_ndev(wil);
int is_ac = power_supply_is_system_supplied();
int rx = atomic_xchg(&wil->isr_count_rx, 0);
int tx = atomic_xchg(&wil->isr_count_tx, 0);
static ulong rxf_old, txf_old;
ulong rxf = ndev->stats.rx_packets;
ulong txf = ndev->stats.tx_packets;
unsigned int i;
/* >0 : AC; 0 : battery; <0 : error */
seq_printf(s, "AC powered : %d\n", is_ac);
seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
rxf_old = rxf;
txf_old = txf;
#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
" " __stringify(x) : ""
for (i = 0; i < ndev->num_tx_queues; i++) {
struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
unsigned long state = txq->state;
seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
CHECK_QSTATE(DRV_XOFF),
CHECK_QSTATE(STACK_XOFF),
CHECK_QSTATE(FROZEN)
);
}
#undef CHECK_QSTATE
return 0;
}
static int wil_info_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_info_debugfs_show, inode->i_private);
}
static const struct file_operations fops_info = {
.open = wil_info_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/*---------Station matrix------------*/
static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
{
@ -630,7 +911,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
else
seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
}
seq_puts(s, "]\n");
seq_printf(s, "] last drop 0x%03x\n", r->ssn_last_drop);
}
static int wil_sta_debugfs_show(struct seq_file *s, void *data)
@ -703,6 +984,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
&wil->secure_pcp);
wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
&wil->status);
wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
HOSTADDR(RGF_USER_USER_ICR));
@ -719,7 +1002,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon);
debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt);
debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi);
debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq);
debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
wil->rgf_blob.data = (void * __force)wil->csr + 0;
wil->rgf_blob.size = 0xa000;

View file

@ -208,6 +208,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
/* Rx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_rx);
return IRQ_HANDLED;
}
@ -246,6 +247,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
/* Tx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_tx);
return IRQ_HANDLED;
}
@ -257,6 +259,7 @@ static void wil_notify_fw_error(struct wil6210_priv *wil)
[1] = "EVENT=FW_ERROR",
[2] = NULL,
};
wil_err(wil, "Notify about firmware error\n");
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
}

View file

@ -61,11 +61,24 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct wil_sta_info *sta = &wil->sta[cid];
wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
sta->status);
sta->data_port_open = false;
if (sta->status != wil_sta_unused) {
wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
/* AP-like interface */
cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
break;
default:
break;
}
sta->status = wil_sta_unused;
}
@ -119,11 +132,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
clear_bit(wil_status_fwconnecting, &wil->status);
break;
default:
/* AP-like interface and monitor:
* never scan, always connected
*/
if (bssid)
cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
break;
}
}
@ -465,6 +473,7 @@ void wil_link_on(struct wil6210_priv *wil)
wil_dbg_misc(wil, "%s()\n", __func__);
netif_carrier_on(ndev);
wil_dbg_misc(wil, "netif_tx_wake : link on\n");
netif_tx_wake_all_queues(ndev);
}
@ -475,6 +484,7 @@ void wil_link_off(struct wil6210_priv *wil)
wil_dbg_misc(wil, "%s()\n", __func__);
netif_tx_stop_all_queues(ndev);
wil_dbg_misc(wil, "netif_tx_stop : link off\n");
netif_carrier_off(ndev);
}
@ -552,6 +562,8 @@ static int __wil_down(struct wil6210_priv *wil)
napi_disable(&wil->napi_tx);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
wil->scan_request);
del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;

View file

@ -15,7 +15,6 @@
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
@ -27,11 +26,22 @@ MODULE_PARM_DESC(use_msi,
" Use MSI interrupt: "
"0 - don't, 1 - (default) - single, or 3");
static bool debug_fw; /* = false; */
module_param(debug_fw, bool, S_IRUGO);
MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
int rc;
/* on platforms with buggy ACPI, pdev->msi_enabled may be set to
* allow pci_enable_device to work. This indicates INTx was not routed
* and only MSI should be used
*/
int msi_only = pdev->msi_enabled;
pdev->msi_enabled = 0;
pci_set_master(pdev);
@ -63,6 +73,12 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
wil->n_msi = use_msi;
if ((wil->n_msi == 0) && msi_only) {
wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
rc = -ENODEV;
goto stop_master;
}
rc = wil6210_init_irq(wil, pdev->irq);
if (rc)
goto stop_master;
@ -71,6 +87,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
mutex_lock(&wil->mutex);
rc = wil_reset(wil);
mutex_unlock(&wil->mutex);
if (debug_fw)
rc = 0;
if (rc)
goto release_irq;
@ -119,9 +137,16 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
rc = pci_enable_device(pdev);
if (rc) {
dev_err(&pdev->dev, "pci_enable_device failed\n");
return -ENODEV;
dev_err(&pdev->dev,
"pci_enable_device failed, retry with MSI only\n");
/* Work around for platforms that can't allocate IRQ:
* retry with MSI only
*/
pdev->msi_enabled = 1;
rc = pci_enable_device(pdev);
}
if (rc)
return -ENODEV;
/* rollback to err_disable_pdev */
rc = pci_request_region(pdev, 0, WIL_NAME);

View file

@ -116,6 +116,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
/* frame with out of date sequence number */
if (seq_less(seq, r->head_seq_num)) {
r->ssn_last_drop = seq;
dev_kfree_skb(skb);
goto out;
}

View file

@ -525,6 +525,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
ndev->stats.rx_bytes += len;
stats->rx_bytes += len;
}
{
static const char * const gro_res_str[] = {
[GRO_MERGED] = "GRO_MERGED",
[GRO_MERGED_FREE] = "GRO_MERGED_FREE",
[GRO_HELD] = "GRO_HELD",
[GRO_NORMAL] = "GRO_NORMAL",
[GRO_DROP] = "GRO_DROP",
};
wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
len, gro_res_str[rc]);
}
}
/**
@ -760,7 +771,7 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
goto found;
}
wil_err(wil, "Tx while no vrings active?\n");
wil_dbg_txrx(wil, "Tx while no vrings active?\n");
return NULL;
@ -881,6 +892,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
int nr_frags = skb_shinfo(skb)->nr_frags;
uint f = 0;
int vring_index = vring - wil->vring_tx;
struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
uint i = swhead;
dma_addr_t pa;
@ -953,6 +965,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
if (wil_vring_is_empty(vring)) /* performance monitoring */
txdata->idle += get_cycles() - txdata->last_idle;
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
@ -1016,15 +1031,17 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
vring = wil_tx_bcast(wil, skb);
}
if (!vring) {
wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
/* do we still have enough room in the vring? */
if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) {
netif_tx_stop_all_queues(wil_to_ndev(wil));
wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
}
switch (rc) {
case 0:
@ -1132,8 +1149,16 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
done++;
}
}
if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
if (wil_vring_is_empty(vring)) { /* performance monitoring */
wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid);
txdata->last_idle = get_cycles();
}
if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) {
wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n");
netif_tx_wake_all_queues(wil_to_ndev(wil));
}
return done;
}

View file

@ -20,6 +20,7 @@
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <linux/timex.h>
#define WIL_NAME "wil6210"
@ -251,7 +252,7 @@ struct vring {
*/
struct vring_tx_data {
int enabled;
cycles_t idle, last_idle, begin;
};
enum { /* for wil6210_priv.status */
@ -303,6 +304,7 @@ struct wil_tid_ampdu_rx {
u16 ssn;
u16 buf_size;
u16 timeout;
u16 ssn_last_drop;
u8 dialog_token;
bool first_time; /* is it 1-st time this buffer used? */
};
@ -410,6 +412,7 @@ struct wil6210_priv {
struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
/* statistics */
struct wil6210_stats stats;
atomic_t isr_count_rx, isr_count_tx;
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper fw_code_blob;
@ -504,9 +507,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
void wil6210_enable_irq(struct wil6210_priv *wil);
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
int wil6210_debugfs_init(struct wil6210_priv *wil);
void wil6210_debugfs_remove(struct wil6210_priv *wil);
int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
struct station_info *sinfo);
struct wireless_dev *wil_cfg80211_init(struct device *dev);
void wil_wdev_free(struct wil6210_priv *wil);

View file

@ -75,6 +75,7 @@ static const struct {
{0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
{0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
{0x880000, 0x88a000, 0x880000}, /* various RGF */
{0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */
{0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
/*
* 920000..930000 ucode code RAM
@ -327,6 +328,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
struct cfg80211_bss *bss;
u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
u.beacon.variable);
wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf);
wil_dbg_wmi(wil, "Beacon interval : %d\n", bi);
wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
ie_len, true);
bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
d_len, signal, GFP_KERNEL);
@ -351,6 +363,9 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
bool aborted = (data->status != WMI_SCAN_SUCCESS);
wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
wil->scan_request, aborted);
del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, aborted);
wil->scan_request = NULL;
@ -668,14 +683,12 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
for (n = 0;; n++) {
u16 len;
bool q;
r->head = ioread32(wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx.head));
if (r->tail == r->head) {
if (n == 0)
wil_dbg_wmi(wil, "No events?\n");
return;
}
if (r->tail == r->head)
break;
wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
r->head, r->tail);
@ -684,14 +697,14 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
sizeof(struct wil6210_mbox_ring_desc));
if (d_tail.sync == 0) {
wil_err(wil, "Mbox evt not owned by FW?\n");
return;
break;
}
/* read cmd header from descriptor */
if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
wil_err(wil, "Mbox evt at 0x%08x?\n",
le32_to_cpu(d_tail.addr));
return;
break;
}
len = le16_to_cpu(hdr.len);
wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
@ -705,7 +718,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
event.wmi) + len, 4),
GFP_KERNEL);
if (!evt)
return;
break;
evt->event.hdr = hdr;
cmd = (void *)&evt->event.wmi;
@ -737,14 +750,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
list_add_tail(&evt->list, &wil->pending_wmi_ev);
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
{
int q = queue_work(wil->wmi_wq,
&wil->wmi_event_worker);
wil_dbg_wmi(wil, "queue_work -> %d\n", q);
}
q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
wil_dbg_wmi(wil, "queue_work -> %d\n", q);
}
if (n > 1)
wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
/* normally, 1 event per IRQ should be processed */
wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
}
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,

View file

@ -122,6 +122,15 @@ config B43_PIO
select SSB_BLOCKIO
default y
config B43_PHY_G
bool "Support for G-PHY (802.11g) devices"
depends on B43 && B43_SSB
default y
---help---
This PHY type can be found in the following chipsets:
PCI: BCM4306, BCM4311, BCM4318
SoC: BCM4712, BCM5352E
config B43_PHY_N
bool "Support for 802.11n (N-PHY) devices"
depends on B43

View file

@ -1,13 +1,11 @@
b43-y += main.o
b43-y += bus.o
b43-y += tables.o
b43-$(CONFIG_B43_PHY_G) += phy_a.o phy_g.o tables.o lo.o wa.o
b43-$(CONFIG_B43_PHY_N) += tables_nphy.o
b43-$(CONFIG_B43_PHY_N) += radio_2055.o
b43-$(CONFIG_B43_PHY_N) += radio_2056.o
b43-$(CONFIG_B43_PHY_N) += radio_2057.o
b43-y += phy_common.o
b43-y += phy_g.o
b43-y += phy_a.o
b43-$(CONFIG_B43_PHY_N) += phy_n.o
b43-$(CONFIG_B43_PHY_LP) += phy_lp.o
b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o
@ -17,8 +15,6 @@ b43-$(CONFIG_B43_PHY_HT) += radio_2059.o
b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o
b43-y += sysfs.o
b43-y += xmit.o
b43-y += lo.o
b43-y += wa.o
b43-y += dma.o
b43-y += pio.o
b43-y += rfkill.o

View file

@ -122,7 +122,11 @@ static const struct bcma_device_id b43_bcma_tbl[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS),
BCMA_CORETABLE_END
};
MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl);
@ -2201,52 +2205,82 @@ err_format:
return -EPROTO;
}
/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */
static int b43_try_request_fw(struct b43_request_fw_context *ctx)
{
struct b43_wldev *dev = ctx->dev;
struct b43_firmware *fw = &ctx->dev->fw;
struct b43_phy *phy = &dev->phy;
const u8 rev = ctx->dev->dev->core_rev;
const char *filename;
u32 tmshigh;
int err;
/* Files for HT and LCN were found by trying one by one */
/* Get microcode */
if ((rev >= 5) && (rev <= 10)) {
filename = "ucode5";
} else if ((rev >= 11) && (rev <= 12)) {
filename = "ucode11";
} else if (rev == 13) {
filename = "ucode13";
} else if (rev == 14) {
filename = "ucode14";
} else if (rev == 15) {
filename = NULL;
switch (rev) {
case 42:
if (phy->type == B43_PHYTYPE_AC)
filename = "ucode42";
break;
case 40:
if (phy->type == B43_PHYTYPE_AC)
filename = "ucode40";
break;
case 33:
if (phy->type == B43_PHYTYPE_LCN40)
filename = "ucode33_lcn40";
break;
case 30:
if (phy->type == B43_PHYTYPE_N)
filename = "ucode30_mimo";
break;
case 29:
if (phy->type == B43_PHYTYPE_HT)
filename = "ucode29_mimo";
break;
case 26:
if (phy->type == B43_PHYTYPE_HT)
filename = "ucode26_mimo";
break;
case 28:
case 25:
if (phy->type == B43_PHYTYPE_N)
filename = "ucode25_mimo";
else if (phy->type == B43_PHYTYPE_LCN)
filename = "ucode25_lcn";
break;
case 24:
if (phy->type == B43_PHYTYPE_LCN)
filename = "ucode24_lcn";
break;
case 23:
if (phy->type == B43_PHYTYPE_N)
filename = "ucode16_mimo";
break;
case 16 ... 19:
if (phy->type == B43_PHYTYPE_N)
filename = "ucode16_mimo";
else if (phy->type == B43_PHYTYPE_LP)
filename = "ucode16_lp";
break;
case 15:
filename = "ucode15";
} else {
switch (dev->phy.type) {
case B43_PHYTYPE_N:
if (rev >= 16)
filename = "ucode16_mimo";
else
goto err_no_ucode;
break;
case B43_PHYTYPE_HT:
if (rev == 29)
filename = "ucode29_mimo";
else
goto err_no_ucode;
break;
case B43_PHYTYPE_LCN:
if (rev == 24)
filename = "ucode24_mimo";
else
goto err_no_ucode;
break;
default:
goto err_no_ucode;
}
break;
case 14:
filename = "ucode14";
break;
case 13:
filename = "ucode13";
break;
case 11 ... 12:
filename = "ucode11";
break;
case 5 ... 10:
filename = "ucode5";
break;
}
if (!filename)
goto err_no_ucode;
err = b43_do_request_fw(ctx, filename, &fw->ucode, true);
if (err)
goto err_load;
@ -2268,117 +2302,121 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
goto err_load;
/* Get initvals */
filename = NULL;
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1initvals5";
else
filename = "a0g0initvals5";
} else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
filename = "b0g0initvals5";
else if (rev >= 13)
if (rev == 13)
filename = "b0g0initvals13";
else
goto err_no_initvals;
else if (rev >= 5 && rev <= 10)
filename = "b0g0initvals5";
break;
case B43_PHYTYPE_N:
if (rev >= 16)
if (rev == 30)
filename = "n16initvals30";
else if (rev == 28 || rev == 25)
filename = "n0initvals25";
else if (rev == 24)
filename = "n0initvals24";
else if (rev == 23)
filename = "n0initvals16"; /* What about n0initvals22? */
else if (rev >= 16 && rev <= 18)
filename = "n0initvals16";
else if ((rev >= 11) && (rev <= 12))
else if (rev >= 11 && rev <= 12)
filename = "n0initvals11";
else
goto err_no_initvals;
break;
case B43_PHYTYPE_LP:
if (rev == 13)
filename = "lp0initvals13";
if (rev >= 16 && rev <= 18)
filename = "lp0initvals16";
else if (rev == 15)
filename = "lp0initvals15";
else if (rev == 14)
filename = "lp0initvals14";
else if (rev >= 15)
filename = "lp0initvals15";
else
goto err_no_initvals;
else if (rev == 13)
filename = "lp0initvals13";
break;
case B43_PHYTYPE_HT:
if (rev == 29)
filename = "ht0initvals29";
else
goto err_no_initvals;
else if (rev == 26)
filename = "ht0initvals26";
break;
case B43_PHYTYPE_LCN:
if (rev == 24)
filename = "lcn0initvals24";
else
goto err_no_initvals;
break;
default:
goto err_no_initvals;
case B43_PHYTYPE_LCN40:
if (rev == 33)
filename = "lcn400initvals33";
break;
case B43_PHYTYPE_AC:
if (rev == 42)
filename = "ac1initvals42";
else if (rev == 40)
filename = "ac0initvals40";
break;
}
if (!filename)
goto err_no_initvals;
err = b43_do_request_fw(ctx, filename, &fw->initvals, false);
if (err)
goto err_load;
/* Get bandswitch initvals */
filename = NULL;
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1bsinitvals5";
else
filename = "a0g0bsinitvals5";
} else if (rev >= 11)
filename = NULL;
else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
if (rev == 13)
filename = "b0g0bsinitvals13";
else if (rev >= 5 && rev <= 10)
filename = "b0g0bsinitvals5";
else if (rev >= 11)
filename = NULL;
else
goto err_no_initvals;
break;
case B43_PHYTYPE_N:
if (rev >= 16)
if (rev == 30)
filename = "n16bsinitvals30";
else if (rev == 28 || rev == 25)
filename = "n0bsinitvals25";
else if (rev == 24)
filename = "n0bsinitvals24";
else if (rev == 23)
filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */
else if (rev >= 16 && rev <= 18)
filename = "n0bsinitvals16";
else if ((rev >= 11) && (rev <= 12))
else if (rev >= 11 && rev <= 12)
filename = "n0bsinitvals11";
else
goto err_no_initvals;
break;
case B43_PHYTYPE_LP:
if (rev == 13)
filename = "lp0bsinitvals13";
if (rev >= 16 && rev <= 18)
filename = "lp0bsinitvals16";
else if (rev == 15)
filename = "lp0bsinitvals15";
else if (rev == 14)
filename = "lp0bsinitvals14";
else if (rev >= 15)
filename = "lp0bsinitvals15";
else
goto err_no_initvals;
else if (rev == 13)
filename = "lp0bsinitvals13";
break;
case B43_PHYTYPE_HT:
if (rev == 29)
filename = "ht0bsinitvals29";
else
goto err_no_initvals;
else if (rev == 26)
filename = "ht0bsinitvals26";
break;
case B43_PHYTYPE_LCN:
if (rev == 24)
filename = "lcn0bsinitvals24";
else
goto err_no_initvals;
break;
default:
goto err_no_initvals;
case B43_PHYTYPE_LCN40:
if (rev == 33)
filename = "lcn400bsinitvals33";
break;
case B43_PHYTYPE_AC:
if (rev == 42)
filename = "ac1bsinitvals42";
else if (rev == 40)
filename = "ac0bsinitvals40";
break;
}
if (!filename)
goto err_no_initvals;
err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);
if (err)
goto err_load;
@ -3798,39 +3836,30 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
struct b43_phy *phy;
struct b43_wldev *dev = wl->current_dev;
struct b43_phy *phy = &dev->phy;
struct ieee80211_conf *conf = &hw->conf;
int antenna;
int err = 0;
bool reload_bss = false;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
b43_mac_suspend(dev);
/* Switch the band (if necessary). This might change the active core. */
err = b43_switch_band(dev, conf->chandef.chan);
if (err)
goto out_unlock_mutex;
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
phy->chandef = &conf->chandef;
phy->channel = conf->chandef.chan->hw_value;
/* Need to reload all settings if the core changed */
if (dev != wl->current_dev) {
dev = wl->current_dev;
changed = ~0;
reload_bss = true;
/* Switch the band (if necessary). */
err = b43_switch_band(dev, conf->chandef.chan);
if (err)
goto out_mac_enable;
/* Switch to the requested channel.
* The firmware takes care of races with the TX handler.
*/
b43_switch_channel(dev, phy->channel);
}
phy = &dev->phy;
if (conf_is_ht(conf))
phy->is_40mhz =
(conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf));
else
phy->is_40mhz = false;
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
conf->long_frame_max_tx_count);
@ -3838,11 +3867,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
if (!changed)
goto out_mac_enable;
/* Switch to the requested channel.
* The firmware takes care of races with the TX handler. */
if (conf->chandef.chan->hw_value != phy->channel)
b43_switch_channel(dev, conf->chandef.chan->hw_value);
dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
/* Adjust the desired TX power level. */
@ -3878,12 +3902,8 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
out_mac_enable:
b43_mac_enable(dev);
out_unlock_mutex:
mutex_unlock(&wl->mutex);
if (wl->vif && reload_bss)
b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
return err;
}
@ -4309,6 +4329,7 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type)
static int b43_phy_versioning(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
const u8 core_rev = dev->dev->core_rev;
u32 tmp;
u8 analog_type;
u8 phy_type;
@ -4323,20 +4344,20 @@ static int b43_phy_versioning(struct b43_wldev *dev)
analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT;
phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT;
phy_rev = (tmp & B43_PHYVER_VERSION);
/* LCNXN is continuation of N which run out of revisions */
if (phy_type == B43_PHYTYPE_LCNXN) {
phy_type = B43_PHYTYPE_N;
phy_rev += 16;
}
switch (phy_type) {
case B43_PHYTYPE_A:
if (phy_rev >= 4)
unsupported = 1;
break;
case B43_PHYTYPE_B:
if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6
&& phy_rev != 7)
unsupported = 1;
break;
#ifdef CONFIG_B43_PHY_G
case B43_PHYTYPE_G:
if (phy_rev > 9)
unsupported = 1;
break;
#endif
#ifdef CONFIG_B43_PHY_N
case B43_PHYTYPE_N:
if (phy_rev > 9)
@ -4374,7 +4395,15 @@ static int b43_phy_versioning(struct b43_wldev *dev)
analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev);
/* Get RADIO versioning */
if (dev->dev->core_rev >= 24) {
if (core_rev == 40 || core_rev == 42) {
radio_manuf = 0x17F;
b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0);
radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA);
} else if (core_rev >= 24) {
u16 radio24[3];
for (tmp = 0; tmp < 3; tmp++) {

View file

@ -573,7 +573,7 @@ static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev)
{//TODO
}
const struct b43_phy_operations b43_phyops_a = {
static const struct b43_phy_operations b43_phyops_a = {
.allocate = b43_aphy_op_allocate,
.free = b43_aphy_op_free,
.prepare_structs = b43_aphy_op_prepare_structs,

View file

@ -123,8 +123,4 @@ struct b43_phy_a {
*/
void b43_phy_inita(struct b43_wldev *dev);
struct b43_phy_operations;
extern const struct b43_phy_operations b43_phyops_a;
#endif /* LINUX_B43_PHY_A_H_ */

View file

@ -45,11 +45,10 @@ int b43_phy_allocate(struct b43_wldev *dev)
phy->ops = NULL;
switch (phy->type) {
case B43_PHYTYPE_A:
phy->ops = &b43_phyops_a;
break;
case B43_PHYTYPE_G:
#ifdef CONFIG_B43_PHY_G
phy->ops = &b43_phyops_g;
#endif
break;
case B43_PHYTYPE_N:
#ifdef CONFIG_B43_PHY_N
@ -94,7 +93,13 @@ int b43_phy_init(struct b43_wldev *dev)
const struct b43_phy_operations *ops = phy->ops;
int err;
phy->channel = ops->get_default_chan(dev);
/* During PHY init we need to use some channel. On the first init this
* function is called *before* b43_op_config, so our pointer is NULL.
*/
if (!phy->chandef) {
phy->chandef = &dev->wl->hw->conf.chandef;
phy->channel = phy->chandef->chan->hw_value;
}
phy->ops->switch_analog(dev, true);
b43_software_rfkill(dev, false);
@ -106,9 +111,7 @@ int b43_phy_init(struct b43_wldev *dev)
}
phy->do_full_init = false;
/* Make sure to switch hardware and firmware (SHM) to
* the default channel. */
err = b43_switch_channel(dev, ops->get_default_chan(dev));
err = b43_switch_channel(dev, phy->channel);
if (err) {
b43err(dev->wl, "PHY init: Channel switch to default failed\n");
goto err_phy_exit;
@ -408,9 +411,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
u16 channelcookie, savedcookie;
int err;
if (new_channel == B43_DEFAULT_CHANNEL)
new_channel = phy->ops->get_default_chan(dev);
/* First we set the channel radio code to prevent the
* firmware from sending ghost packets.
*/
@ -428,7 +428,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
if (err)
goto err_restore_cookie;
dev->phy.channel = new_channel;
/* Wait for the radio to tune to the channel and stabilize. */
msleep(8);
@ -547,10 +546,9 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on)
}
bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
bool b43_is_40mhz(struct b43_wldev *dev)
{
return (channel_type == NL80211_CHAN_HT40MINUS ||
channel_type == NL80211_CHAN_HT40PLUS);
return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40;
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */

View file

@ -228,9 +228,6 @@ struct b43_phy {
bool supports_2ghz;
bool supports_5ghz;
/* HT info */
bool is_40mhz;
/* Is GMODE (2 GHz mode) bit enabled? */
bool gmode;
@ -267,9 +264,8 @@ struct b43_phy {
unsigned long next_txpwr_check_time;
/* Current channel */
struct cfg80211_chan_def *chandef;
unsigned int channel;
u16 channel_freq;
enum nl80211_channel_type channel_type;
/* PHY TX errors counter. */
atomic_t txerr_cnt;
@ -400,10 +396,6 @@ void b43_phy_take_out_of_reset(struct b43_wldev *dev);
* b43_switch_channel - Switch to another channel
*/
int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
/**
* B43_DEFAULT_CHANNEL - Switch to the default channel.
*/
#define B43_DEFAULT_CHANNEL UINT_MAX
/**
* b43_software_rfkill - Turn the radio ON or OFF in software.
@ -454,7 +446,7 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset);
*/
void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on);
bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
bool b43_is_40mhz(struct b43_wldev *dev);
void b43_phy_force_clock(struct b43_wldev *dev, bool force);

View file

@ -596,7 +596,7 @@ static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev)
u8 target[3];
s16 a1[3], b0[3], b1[3];
u16 freq = dev->phy.channel_freq;
u16 freq = dev->phy.chandef->chan->center_freq;
int i, c;
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {

View file

@ -590,7 +590,103 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
* Radio 0x2057
**************************************************/
/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */
static void b43_radio_2057_chantab_upload(struct b43_wldev *dev,
const struct b43_nphy_chantabent_rev7 *e_r7,
const struct b43_nphy_chantabent_rev7_2g *e_r7_2g)
{
if (e_r7_2g) {
b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0);
b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1);
b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1);
b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac);
b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0);
b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1);
b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune);
b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune);
b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune);
b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0);
b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0);
b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0);
b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1);
b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1);
b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1);
} else {
b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0);
b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1);
b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1);
b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac);
b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0);
b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1);
b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune);
b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune);
b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune);
b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune);
b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune);
b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0);
b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0);
b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0);
b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0);
b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0);
b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0);
b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0);
b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1);
b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1);
b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1);
b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1);
b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1);
b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1);
b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1);
}
}
static void b43_radio_2057_setup(struct b43_wldev *dev,
const struct b43_nphy_chantabent_rev7 *tabent_r7,
const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g)
{
struct b43_phy *phy = &dev->phy;
b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g);
switch (phy->radio_rev) {
case 0 ... 4:
case 6:
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f);
b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
} else {
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f);
b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
}
break;
/* TODO */
}
/* TODO */
usleep_range(50, 100);
/* VCO calibration */
b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01);
b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04);
b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4);
b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01);
usleep_range(300, 600);
}
/* Calibrate resistors in LPF of PLL?
* http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal
*/
static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
@ -603,15 +699,25 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1);
}
/* Enable */
b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1);
udelay(10);
b43_radio_set(dev, R2057_RCAL_CONFIG, 0x3);
if (!b43_radio_wait_value(dev, R2057_RCCAL_N1_1, 1, 1, 100, 1000000)) {
/* Start */
b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2);
usleep_range(100, 200);
/* Stop */
b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
/* Wait and check for result */
if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) {
b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
return 0;
}
b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E;
/* Disable */
b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1);
if (phy->radio_rev == 5) {
@ -627,7 +733,9 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
return tmp & 0x3e;
}
/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal */
/* Calibrate the internal RC oscillator?
* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal
*/
static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
@ -635,49 +743,76 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
phy->radio_rev == 6);
u16 tmp;
/* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0);
} else {
b43_radio_write(dev, 0x1AE, 0x61);
b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1);
}
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
/* Start, wait, stop */
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000))
b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
usleep_range(70, 140);
/* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
} else {
b43_radio_write(dev, 0x1AE, 0x69);
b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5);
}
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
/* Start, wait, stop */
usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
usleep_range(70, 140);
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000))
b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
usleep_range(70, 140);
/* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73);
b43_radio_write(dev, R2057_RCCAL_X1, 0x28);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
} else {
b43_radio_write(dev, 0x1AE, 0x73);
b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73);
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99);
}
/* Start, wait, stop */
usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
usleep_range(70, 140);
if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000)) {
b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
return 0;
}
tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP);
usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
usleep_range(70, 140);
if (special)
b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1);
else
b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1);
return tmp;
}
@ -798,6 +933,7 @@ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev,
static void b43_radio_2056_setup(struct b43_wldev *dev,
const struct b43_nphy_channeltab_entry_rev3 *e)
{
struct b43_phy *phy = &dev->phy;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
enum ieee80211_band band = b43_current_band(dev->wl);
u16 offset;
@ -895,7 +1031,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
offset | B2056_TX_MIXG_BOOST_TUNE,
mixg_boost);
} else {
bias = dev->phy.is_40mhz ? 0x40 : 0x20;
bias = b43_is_40mhz(dev) ? 0x40 : 0x20;
b43_radio_write(dev,
offset | B2056_TX_INTPAG_IMAIN_STAT,
bias);
@ -909,7 +1045,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee);
}
} else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) {
u16 freq = dev->phy.channel_freq;
u16 freq = phy->chandef->chan->center_freq;
if (freq < 5100) {
paa_boost = 0xA;
pada_boost = 0x77;
@ -1210,8 +1346,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
u16 bw, len, rot, angle;
struct b43_c32 *samples;
bw = (dev->phy.is_40mhz) ? 40 : 20;
bw = b43_is_40mhz(dev) ? 40 : 20;
len = bw << 3;
if (test) {
@ -1220,7 +1355,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
else
bw = 80;
if (dev->phy.is_40mhz)
if (b43_is_40mhz(dev))
bw <<= 1;
len = bw << 1;
@ -1248,7 +1383,8 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
u16 wait, bool iqmode, bool dac_test)
u16 wait, bool iqmode, bool dac_test,
bool modify_bbmult)
{
struct b43_phy_n *nphy = dev->phy.n;
int i;
@ -1262,12 +1398,10 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
}
/* TODO: add modify_bbmult argument */
if (!dev->phy.is_40mhz)
tmp = 0x6464;
else
tmp = 0x4747;
b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
if (modify_bbmult) {
tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747;
b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
}
b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
@ -1285,10 +1419,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF);
b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000);
} else {
if (dac_test)
b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5);
else
b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1);
tmp = dac_test ? 5 : 1;
b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp);
}
for (i = 0; i < 100; i++) {
if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) {
@ -1675,6 +1807,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
u16 saved_regs_phy_rfctl[2];
@ -1897,9 +2030,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
/* Remember for which channel we store configuration */
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq;
nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq;
else
nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq;
nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq;
/* End of calibration, restore configuration */
b43_nphy_classifier(dev, 7, class);
@ -2192,7 +2325,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84);
b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84);
if (!dev->phy.is_40mhz) {
if (!b43_is_40mhz(dev)) {
/* Set dwell lengths */
b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B);
b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B);
@ -2206,7 +2339,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21);
if (!dev->phy.is_40mhz) {
if (!b43_is_40mhz(dev)) {
b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1);
b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
@ -2221,12 +2354,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
if (nphy->gain_boost) {
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ &&
dev->phy.is_40mhz)
b43_is_40mhz(dev))
code = 4;
else
code = 5;
} else {
code = dev->phy.is_40mhz ? 6 : 7;
code = b43_is_40mhz(dev) ? 6 : 7;
}
/* Set HPVGA2 index */
@ -2298,7 +2431,7 @@ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev)
static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
{
if (!offset)
offset = (dev->phy.is_40mhz) ? 0x159 : 0x154;
offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
}
@ -2371,13 +2504,13 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159);
lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152);
if (b43_nphy_ipa(dev)) {
if ((phy->radio_rev == 5 && phy->is_40mhz) ||
if ((phy->radio_rev == 5 && b43_is_40mhz(dev)) ||
phy->radio_rev == 7 || phy->radio_rev == 8) {
bcap_val = b43_radio_read(dev, 0x16b);
scap_val = b43_radio_read(dev, 0x16a);
scap_val_11b = scap_val;
bcap_val_11b = bcap_val;
if (phy->radio_rev == 5 && phy->is_40mhz) {
if (phy->radio_rev == 5 && b43_is_40mhz(dev)) {
scap_val_11n_20 = scap_val;
bcap_val_11n_20 = bcap_val;
scap_val_11n_40 = bcap_val_11n_40 = 0xc;
@ -2519,7 +2652,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
}
}
} else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
if (!phy->is_40mhz) {
if (!b43_is_40mhz(dev)) {
b43_radio_write(dev, 0x5F, 0x14);
b43_radio_write(dev, 0xE8, 0x12);
} else {
@ -2528,7 +2661,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
}
}
} else {
u16 freq = phy->channel_freq;
u16 freq = phy->chandef->chan->center_freq;
if ((freq >= 5180 && freq <= 5230) ||
(freq >= 5745 && freq <= 5805)) {
b43_radio_write(dev, 0x7D, 0xFF);
@ -2592,7 +2725,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77);
b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77);
if (!phy->is_40mhz) {
if (!b43_is_40mhz(dev)) {
b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D);
b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D);
} else {
@ -2691,7 +2824,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700);
if (!dev->phy.is_40mhz) {
if (!b43_is_40mhz(dev)) {
b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
} else {
@ -2946,12 +3079,13 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone
*/
static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val,
bool iqmode, bool dac_test)
bool iqmode, bool dac_test, bool modify_bbmult)
{
u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test);
if (samp == 0)
return -1;
b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test);
b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test,
modify_bbmult);
return 0;
}
@ -3114,7 +3248,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
if (dev->phy.rev < 2 && dev->phy.is_40mhz)
if (dev->phy.rev < 2 && b43_is_40mhz(dev))
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
} else {
b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
@ -3168,7 +3302,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
else if (dev->phy.rev < 2)
b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
if (dev->phy.rev < 2 && dev->phy.is_40mhz)
if (dev->phy.rev < 2 && b43_is_40mhz(dev))
b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
if (b43_nphy_ipa(dev)) {
@ -3184,12 +3318,13 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */
static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
u8 txpi[2], bbmult, i;
u16 tmp, radio_gain, dac_gain;
u16 freq = dev->phy.channel_freq;
u16 freq = phy->chandef->chan->center_freq;
u32 txgain;
/* u32 gaintbl; rev3+ */
@ -3234,7 +3369,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
*/
for (i = 0; i < 2; i++) {
txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]);
const u32 *table = b43_nphy_get_tx_gain_table(dev);
if (!table)
break;
txgain = *(table + txpi[i]);
if (dev->phy.rev >= 3)
radio_gain = (txgain >> 16) & 0x1FFFF;
@ -3388,7 +3527,7 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false);
b43_nphy_stop_playback(dev);
b43_nphy_tx_tone(dev, 0xFA0, 0, false, false);
b43_nphy_tx_tone(dev, 4000, 0, false, false, false);
udelay(20);
tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1);
b43_nphy_stop_playback(dev);
@ -3439,21 +3578,21 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
delta = 0;
switch (stf_mode) {
case 0:
if (dev->phy.is_40mhz && dev->phy.rev >= 5) {
if (b43_is_40mhz(dev) && dev->phy.rev >= 5) {
idx = 68;
} else {
delta = 1;
idx = dev->phy.is_40mhz ? 52 : 4;
idx = b43_is_40mhz(dev) ? 52 : 4;
}
break;
case 1:
idx = dev->phy.is_40mhz ? 76 : 28;
idx = b43_is_40mhz(dev) ? 76 : 28;
break;
case 2:
idx = dev->phy.is_40mhz ? 84 : 36;
idx = b43_is_40mhz(dev) ? 84 : 36;
break;
case 3:
idx = dev->phy.is_40mhz ? 92 : 44;
idx = b43_is_40mhz(dev) ? 92 : 44;
break;
}
@ -3474,6 +3613,7 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */
static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@ -3483,7 +3623,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
s32 num, den, pwr;
u32 regval[64];
u16 freq = dev->phy.channel_freq;
u16 freq = phy->chandef->chan->center_freq;
u16 tmp;
u16 r; /* routing */
u8 i, c;
@ -3647,6 +3787,9 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
int i;
table = b43_nphy_get_tx_gain_table(dev);
if (!table)
return;
b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
@ -3705,21 +3848,28 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
}
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */
static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
/*
* TX low-pass filter bandwidth setup
* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw
*/
static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev)
{
u16 tmp;
if (dev->phy.rev >= 3) {
if (b43_nphy_ipa(dev)) {
tmp = 4;
b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
(((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
}
if (dev->phy.rev < 3 || dev->phy.rev >= 7)
return;
tmp = 1;
if (b43_nphy_ipa(dev))
tmp = b43_is_40mhz(dev) ? 5 : 4;
else
tmp = b43_is_40mhz(dev) ? 3 : 1;
b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
(tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
if (b43_nphy_ipa(dev)) {
tmp = b43_is_40mhz(dev) ? 4 : 1;
b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2,
(((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
(tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
}
}
@ -3992,7 +4142,7 @@ static void b43_nphy_spur_workaround(struct b43_wldev *dev)
if (nphy->gband_spurwar_en) {
/* TODO: N PHY Adjust Analog Pfbw (7) */
if (channel == 11 && dev->phy.is_40mhz)
if (channel == 11 && b43_is_40mhz(dev))
; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
else
; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
@ -4286,7 +4436,7 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
b43_phy_write(dev, B43_PHY_N(offset[i] + j),
tbl_tx_filter_coef_rev4[i][j]);
if (dev->phy.is_40mhz) {
if (b43_is_40mhz(dev)) {
for (j = 0; j < 15; j++)
b43_phy_write(dev, B43_PHY_N(offset[0] + j),
tbl_tx_filter_coef_rev4[3][j]);
@ -4345,6 +4495,9 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
for (i = 0; i < 2; ++i) {
table = b43_nphy_get_tx_gain_table(dev);
if (!table)
break;
if (dev->phy.rev >= 3) {
target.ipa[i] = (table[index[i]] >> 16) & 0xF;
target.pad[i] = (table[index[i]] >> 20) & 0xF;
@ -4500,8 +4653,9 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
}
iqcal_chanspec->center_freq = dev->phy.channel_freq;
iqcal_chanspec->channel_type = dev->phy.channel_type;
iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq;
iqcal_chanspec->channel_type =
cfg80211_get_chandef_type(dev->phy.chandef);
b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table);
if (nphy->hang_avoid)
@ -4581,6 +4735,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
struct nphy_txgains target,
bool full, bool mphase)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
int i;
int error = 0;
@ -4621,7 +4776,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
(dev->phy.rev == 5 && nphy->ipa2g_on &&
b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ);
if (phy6or5x) {
if (dev->phy.is_40mhz) {
if (b43_is_40mhz(dev)) {
b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
tbl_tx_iqlo_cal_loft_ladder_40);
b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
@ -4636,16 +4791,16 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
if (!dev->phy.is_40mhz)
if (!b43_is_40mhz(dev))
freq = 2500;
else
freq = 5000;
if (nphy->mphase_cal_phase_id > 2)
b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8,
0xFFFF, 0, true, false);
b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8,
0xFFFF, 0, true, false, false);
else
error = b43_nphy_tx_tone(dev, freq, 250, true, false);
error = b43_nphy_tx_tone(dev, freq, 250, true, false, false);
if (error == 0) {
if (nphy->mphase_cal_phase_id > 2) {
@ -4773,9 +4928,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
nphy->txiqlocal_bestc);
nphy->txiqlocal_coeffsvalid = true;
nphy->txiqlocal_chanspec.center_freq =
dev->phy.channel_freq;
phy->chandef->chan->center_freq;
nphy->txiqlocal_chanspec.channel_type =
dev->phy.channel_type;
cfg80211_get_chandef_type(phy->chandef);
} else {
length = 11;
if (dev->phy.rev < 3)
@ -4811,8 +4966,8 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
bool equal = true;
if (!nphy->txiqlocal_coeffsvalid ||
nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq ||
nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type)
nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq ||
nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef))
return;
b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
@ -4968,11 +5123,11 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
if (playtone) {
ret = b43_nphy_tx_tone(dev, 4000,
(nphy->rxcalparams & 0xFFFF),
false, false);
false, false, true);
playtone = false;
} else {
b43_nphy_run_samples(dev, 160, 0xFFFF, 0,
false, false);
b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false,
false, true);
}
if (ret == 0) {
@ -5344,7 +5499,7 @@ static int b43_phy_initn(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320);
if (phy->rev >= 3 && phy->rev <= 6)
b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032);
b43_nphy_tx_lp_fbw(dev);
b43_nphy_tx_lpf_bw(dev);
if (phy->rev >= 3)
b43_nphy_spur_workaround(dev);
@ -5430,14 +5585,14 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
if (dev->phy.rev < 3)
b43_nphy_adjust_lna_gain_table(dev);
b43_nphy_tx_lp_fbw(dev);
b43_nphy_tx_lpf_bw(dev);
if (dev->phy.rev >= 3 &&
dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) {
bool avoid = false;
if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) {
avoid = true;
} else if (!b43_channel_type_is_40mhz(phy->channel_type)) {
} else if (!b43_is_40mhz(dev)) {
if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
avoid = true;
} else { /* 40MHz */
@ -5484,10 +5639,17 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL;
const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL;
const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL;
const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL;
u8 tmp;
if (dev->phy.rev >= 3) {
if (phy->rev >= 7) {
r2057_get_chantabent_rev7(dev, channel->center_freq,
&tabent_r7, &tabent_r7_2g);
if (!tabent_r7 && !tabent_r7_2g)
return -ESRCH;
} else if (phy->rev >= 3) {
tabent_r3 = b43_nphy_get_chantabent_rev3(dev,
channel->center_freq);
if (!tabent_r3)
@ -5502,20 +5664,36 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
/* Channel is set later in common code, but we need to set it on our
own to let this function's subcalls work properly. */
phy->channel = channel->hw_value;
phy->channel_freq = channel->center_freq;
#if 0
if (b43_channel_type_is_40mhz(phy->channel_type) !=
b43_channel_type_is_40mhz(channel_type))
; /* TODO: BMAC BW Set (channel_type) */
#endif
if (channel_type == NL80211_CHAN_HT40PLUS)
b43_phy_set(dev, B43_NPHY_RXCTL,
B43_NPHY_RXCTL_BSELU20);
else if (channel_type == NL80211_CHAN_HT40MINUS)
b43_phy_mask(dev, B43_NPHY_RXCTL,
~B43_NPHY_RXCTL_BSELU20);
if (channel_type == NL80211_CHAN_HT40PLUS) {
b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20);
if (phy->rev >= 7)
b43_phy_set(dev, 0x310, 0x8000);
} else if (channel_type == NL80211_CHAN_HT40MINUS) {
b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20);
if (phy->rev >= 7)
b43_phy_mask(dev, 0x310, (u16)~0x8000);
}
if (dev->phy.rev >= 3) {
if (phy->rev >= 7) {
const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ?
&(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs);
if (phy->radio_rev <= 4 || phy->radio_rev == 6) {
tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0;
b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp);
b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp);
}
b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g);
b43_nphy_channel_setup(dev, phy_regs, channel);
} else if (phy->rev >= 3) {
tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0;
b43_radio_maskset(dev, 0x08, 0xFFFB, tmp);
b43_radio_2056_setup(dev, tabent_r3);

View file

@ -26,7 +26,7 @@
#include "radio_2057.h"
#include "phy_common.h"
static u16 r2057_rev4_init[42][2] = {
static u16 r2057_rev4_init[][2] = {
{ 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 },
{ 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff },
{ 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 },
@ -40,7 +40,7 @@ static u16 r2057_rev4_init[42][2] = {
{ 0x1AB, 0x00 }, { 0x1AC, 0x00 },
};
static u16 r2057_rev5_init[44][2] = {
static u16 r2057_rev5_init[][2] = {
{ 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
{ 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
{ 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@ -54,7 +54,7 @@ static u16 r2057_rev5_init[44][2] = {
{ 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 },
};
static u16 r2057_rev5a_init[45][2] = {
static u16 r2057_rev5a_init[][2] = {
{ 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
{ 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
{ 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@ -69,7 +69,7 @@ static u16 r2057_rev5a_init[45][2] = {
{ 0x1C2, 0x80 },
};
static u16 r2057_rev7_init[54][2] = {
static u16 r2057_rev7_init[][2] = {
{ 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
{ 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
{ 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 },
@ -86,7 +86,8 @@ static u16 r2057_rev7_init[54][2] = {
{ 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
};
static u16 r2057_rev8_init[54][2] = {
/* TODO: Which devices should use it?
static u16 r2057_rev8_init[][2] = {
{ 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
{ 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
{ 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f },
@ -102,6 +103,47 @@ static u16 r2057_rev8_init[54][2] = {
{ 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 },
{ 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
};
*/
#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
r20, r21, r22, r23, r24, r25, r26, r27) \
.radio_vcocal_countval0 = r00, \
.radio_vcocal_countval1 = r01, \
.radio_rfpll_refmaster_sparextalsize = r02, \
.radio_rfpll_loopfilter_r1 = r03, \
.radio_rfpll_loopfilter_c2 = r04, \
.radio_rfpll_loopfilter_c1 = r05, \
.radio_cp_kpd_idac = r06, \
.radio_rfpll_mmd0 = r07, \
.radio_rfpll_mmd1 = r08, \
.radio_vcobuf_tune = r09, \
.radio_logen_mx2g_tune = r10, \
.radio_logen_mx5g_tune = r11, \
.radio_logen_indbuf2g_tune = r12, \
.radio_logen_indbuf5g_tune = r13, \
.radio_txmix2g_tune_boost_pu_core0 = r14, \
.radio_pad2g_tune_pus_core0 = r15, \
.radio_pga_boost_tune_core0 = r16, \
.radio_txmix5g_boost_tune_core0 = r17, \
.radio_pad5g_tune_misc_pus_core0 = r18, \
.radio_lna2g_tune_core0 = r19, \
.radio_lna5g_tune_core0 = r20, \
.radio_txmix2g_tune_boost_pu_core1 = r21, \
.radio_pad2g_tune_pus_core1 = r22, \
.radio_pga_boost_tune_core1 = r23, \
.radio_txmix5g_boost_tune_core1 = r24, \
.radio_pad5g_tune_misc_pus_core1 = r25, \
.radio_lna2g_tune_core1 = r26, \
.radio_lna5g_tune_core1 = r27
#define PHYREGS(r0, r1, r2, r3, r4, r5) \
.phy_regs.phy_bw1a = r0, \
.phy_regs.phy_bw2 = r1, \
.phy_regs.phy_bw3 = r2, \
.phy_regs.phy_bw4 = r3, \
.phy_regs.phy_bw5 = r4, \
.phy_regs.phy_bw6 = r5
void r2057_upload_inittabs(struct b43_wldev *dev)
{
@ -109,33 +151,69 @@ void r2057_upload_inittabs(struct b43_wldev *dev)
u16 *table = NULL;
u16 size, i;
if (phy->rev == 7) {
switch (phy->rev) {
case 7:
table = r2057_rev4_init[0];
size = ARRAY_SIZE(r2057_rev4_init);
} else if (phy->rev == 8 || phy->rev == 9) {
break;
case 8:
if (phy->radio_rev == 5) {
if (phy->radio_rev == 8) {
table = r2057_rev5_init[0];
size = ARRAY_SIZE(r2057_rev5_init);
} else {
table = r2057_rev5a_init[0];
size = ARRAY_SIZE(r2057_rev5a_init);
}
table = r2057_rev5_init[0];
size = ARRAY_SIZE(r2057_rev5_init);
} else if (phy->radio_rev == 7) {
table = r2057_rev7_init[0];
size = ARRAY_SIZE(r2057_rev7_init);
} else if (phy->radio_rev == 9) {
table = r2057_rev8_init[0];
size = ARRAY_SIZE(r2057_rev8_init);
}
break;
case 9:
if (phy->radio_rev == 5) {
table = r2057_rev5a_init[0];
size = ARRAY_SIZE(r2057_rev5a_init);
}
break;
}
B43_WARN_ON(!table);
if (table) {
for (i = 0; i < 10; i++) {
pr_info("radio_write 0x%X ", *table);
table++;
pr_info("0x%X\n", *table);
table++;
}
for (i = 0; i < size; i++, table += 2)
b43_radio_write(dev, table[0], table[1]);
}
}
void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
const struct b43_nphy_chantabent_rev7 **tabent_r7,
const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g)
{
struct b43_phy *phy = &dev->phy;
const struct b43_nphy_chantabent_rev7 *e_r7 = NULL;
const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL;
unsigned int len, i;
*tabent_r7 = NULL;
*tabent_r7_2g = NULL;
/* TODO */
switch (phy->rev) {
default:
break;
}
if (e_r7) {
for (i = 0; i < len; i++, e_r7++) {
if (e_r7->freq == freq) {
*tabent_r7 = e_r7;
return;
}
}
} else if (e_r7_2g) {
for (i = 0; i < len; i++, e_r7_2g++) {
if (e_r7_2g->freq == freq) {
*tabent_r7_2g = e_r7_2g;
return;
}
}
} else {
B43_WARN_ON(1);
}
}

View file

@ -425,6 +425,72 @@
#define R2057_VCM_MASK 0x7
struct b43_nphy_chantabent_rev7 {
/* The channel frequency in MHz */
u16 freq;
/* Radio regs values on channelswitch */
u8 radio_vcocal_countval0;
u8 radio_vcocal_countval1;
u8 radio_rfpll_refmaster_sparextalsize;
u8 radio_rfpll_loopfilter_r1;
u8 radio_rfpll_loopfilter_c2;
u8 radio_rfpll_loopfilter_c1;
u8 radio_cp_kpd_idac;
u8 radio_rfpll_mmd0;
u8 radio_rfpll_mmd1;
u8 radio_vcobuf_tune;
u8 radio_logen_mx2g_tune;
u8 radio_logen_mx5g_tune;
u8 radio_logen_indbuf2g_tune;
u8 radio_logen_indbuf5g_tune;
u8 radio_txmix2g_tune_boost_pu_core0;
u8 radio_pad2g_tune_pus_core0;
u8 radio_pga_boost_tune_core0;
u8 radio_txmix5g_boost_tune_core0;
u8 radio_pad5g_tune_misc_pus_core0;
u8 radio_lna2g_tune_core0;
u8 radio_lna5g_tune_core0;
u8 radio_txmix2g_tune_boost_pu_core1;
u8 radio_pad2g_tune_pus_core1;
u8 radio_pga_boost_tune_core1;
u8 radio_txmix5g_boost_tune_core1;
u8 radio_pad5g_tune_misc_pus_core1;
u8 radio_lna2g_tune_core1;
u8 radio_lna5g_tune_core1;
/* PHY res values on channelswitch */
struct b43_phy_n_sfo_cfg phy_regs;
};
struct b43_nphy_chantabent_rev7_2g {
/* The channel frequency in MHz */
u16 freq;
/* Radio regs values on channelswitch */
u8 radio_vcocal_countval0;
u8 radio_vcocal_countval1;
u8 radio_rfpll_refmaster_sparextalsize;
u8 radio_rfpll_loopfilter_r1;
u8 radio_rfpll_loopfilter_c2;
u8 radio_rfpll_loopfilter_c1;
u8 radio_cp_kpd_idac;
u8 radio_rfpll_mmd0;
u8 radio_rfpll_mmd1;
u8 radio_vcobuf_tune;
u8 radio_logen_mx2g_tune;
u8 radio_logen_indbuf2g_tune;
u8 radio_txmix2g_tune_boost_pu_core0;
u8 radio_pad2g_tune_pus_core0;
u8 radio_lna2g_tune_core0;
u8 radio_txmix2g_tune_boost_pu_core1;
u8 radio_pad2g_tune_pus_core1;
u8 radio_lna2g_tune_core1;
/* PHY regs values on channelswitch */
struct b43_phy_n_sfo_cfg phy_regs;
};
void r2057_upload_inittabs(struct b43_wldev *dev);
void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
const struct b43_nphy_chantabent_rev7 **tabent_r7,
const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g);
#endif /* B43_RADIO_2057_H_ */

View file

@ -2146,7 +2146,196 @@ static const u16 b43_ntab_antswctl_r3[4][32] = {
}
};
/* TX gain tables */
/* static tables, PHY revision >= 7 */
/* Copied from brcmsmac (5.75.11) */
static const u32 b43_ntab_tmap_r7[] = {
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
0xf1111110, 0x11111111, 0x11f11111, 0x00011111,
0x11110000, 0x1111f111, 0x11111111, 0x111111f1,
0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa,
0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88,
0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888,
0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808,
0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08,
0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080,
0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
0x22000000, 0x2222b222, 0x22222222, 0x222222b2,
0xb2222220, 0x22222222, 0x22d22222, 0x00000222,
0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
0x33000000, 0x3333b333, 0x33333333, 0x333333b3,
0xb3333330, 0x33333333, 0x33d33333, 0x00000333,
0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
0x22222200, 0x2222f222, 0x22222222, 0x222222f2,
0x22222222, 0x22222222, 0x22f22222, 0x00000222,
0x11000000, 0x1111f111, 0x11111111, 0x11111111,
0xf1111111, 0x11111111, 0x11f11111, 0x01111111,
0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b,
0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb,
0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa,
0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb,
0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999,
0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88,
0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a,
0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b,
0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909,
0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08,
0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a,
0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090,
0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090,
0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080,
0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0,
0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
0x33000000, 0x3333f333, 0x33333333, 0x333333f3,
0xf3333330, 0x33333333, 0x33f33333, 0x00000333,
0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
/* Extracted from MMIO dump of 6.30.223.141 */
static const u32 b43_ntab_noisevar_r7[] = {
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
};
/**************************************************
* TX gain tables
**************************************************/
static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42,
0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44,
@ -2182,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
0x03801442, 0x03801344, 0x03801342, 0x00002b00,
};
static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
/* EPA 2 GHz */
static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = {
0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e,
0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037,
0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e,
@ -2217,7 +2408,9 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
0x1041003c, 0x1041003b, 0x10410039, 0x10410037,
};
static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
/* EPA 5 GHz */
static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = {
0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e,
0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037,
0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e,
@ -2252,7 +2445,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037,
};
static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = {
0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e,
0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037,
0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e,
@ -2287,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034,
};
static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = {
0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044,
0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c,
0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e,
@ -2322,7 +2515,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
0x0062003b, 0x00620039, 0x00620037, 0x00620035,
};
static const u32 txpwrctrl_tx_gain_ipa[] = {
/* IPA 2 GHz */
static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = {
0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029,
0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025,
0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029,
@ -2357,7 +2552,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = {
0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025,
};
static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = {
0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029,
0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025,
0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029,
@ -2392,7 +2587,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025,
};
static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = {
0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029,
0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025,
0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029,
@ -2427,7 +2622,45 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025,
};
static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
/* Extracted from MMIO dump of 6.30.223.141 */
static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = {
0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029,
0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b,
0x6087002e, 0x60770031, 0x606f0032, 0x60670034,
0x60670031, 0x605f0033, 0x605f0031, 0x60570033,
0x60570030, 0x6057002d, 0x6057002b, 0x604f002d,
0x604f002b, 0x604f0029, 0x604f0026, 0x60470029,
0x60470027, 0x603f0029, 0x603f0027, 0x603f0025,
0x60370029, 0x60370027, 0x60370024, 0x602f002a,
0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a,
0x60270028, 0x60270026, 0x60270024, 0x60270022,
0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024,
0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d,
0x60170029, 0x60170027, 0x60170025, 0x60170023,
0x60170021, 0x6017001f, 0x6017001d, 0x6017001c,
0x6017001a, 0x60170018, 0x60170018, 0x60170016,
0x60170015, 0x600f0029, 0x600f0027, 0x600f0025,
0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d,
0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018,
0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215,
0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
};
/* IPA 2 5Hz */
static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = {
0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031,
0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b,
0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027,
@ -2462,6 +2695,42 @@ static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f,
};
/* Extracted from MMIO dump of 6.30.223.141 */
static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = {
0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f,
0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030,
0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032,
0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030,
0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b,
0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b,
0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029,
0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032,
0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031,
0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031,
0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030,
0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f,
0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d,
0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d,
0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c,
0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023,
0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c,
0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016,
0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012,
0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e,
0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b,
0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008,
0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007,
0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006,
0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004,
0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003,
0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001,
0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001,
};
const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = {
-114, -108, -98, -91, -84, -78, -70, -62,
-54, -46, -39, -31, -23, -15, -8, 0
@ -3031,6 +3300,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset,
b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
} while (0)
static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev)
{
ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
}
static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev)
{
struct ssb_sprom *sprom = dev->dev->bus_sprom;
u8 antswlut;
int core, offset, i;
const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */
const u8 antswlut0_values[][3] = {
{ 0x2, 0x12, 0x8 }, /* Core 0 */
{ 0x2, 0x18, 0x2 }, /* Core 1 */
};
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
antswlut = sprom->fem.ghz5.antswlut;
else
antswlut = sprom->fem.ghz2.antswlut;
switch (antswlut) {
case 0:
for (core = 0; core < 2; core++) {
for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) {
offset = core ? 0x20 : 0x00;
offset += antswlut0_offsets[i];
b43_ntab_write(dev, B43_NTAB8(9, offset),
antswlut0_values[core][i]);
}
}
break;
default:
b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut);
break;
}
}
static void b43_nphy_tables_init_rev16(struct b43_wldev *dev)
{
/* Static tables */
if (dev->phy.do_full_init) {
ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
b43_nphy_tables_init_shared_lut(dev);
}
/* Volatile tables */
b43_nphy_tables_init_rev7_volatile(dev);
}
static void b43_nphy_tables_init_rev7(struct b43_wldev *dev)
{
/* Static tables */
if (dev->phy.do_full_init) {
ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7);
ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
b43_nphy_tables_init_shared_lut(dev);
}
/* Volatile tables */
b43_nphy_tables_init_rev7_volatile(dev);
}
static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
{
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@ -3057,16 +3411,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
b43_nphy_tables_init_shared_lut(dev);
}
/* Volatile tables */
@ -3115,7 +3460,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */
void b43_nphy_tables_init(struct b43_wldev *dev)
{
if (dev->phy.rev >= 3)
if (dev->phy.rev >= 16)
b43_nphy_tables_init_rev16(dev);
else if (dev->phy.rev >= 7)
b43_nphy_tables_init_rev7(dev);
else if (dev->phy.rev >= 3)
b43_nphy_tables_init_rev3(dev);
else
b43_nphy_tables_init_rev0(dev);
@ -3124,23 +3473,45 @@ void b43_nphy_tables_init(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */
static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
if (dev->phy.rev >= 6) {
if (dev->dev->chip_id == 47162)
return txpwrctrl_tx_gain_ipa_rev5;
return txpwrctrl_tx_gain_ipa_rev6;
} else if (dev->phy.rev >= 5) {
return txpwrctrl_tx_gain_ipa_rev5;
} else {
return txpwrctrl_tx_gain_ipa;
switch (phy->rev) {
case 16:
if (phy->radio_rev == 9)
return b43_ntab_tx_gain_ipa_2057_rev9_2g;
case 6:
if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162)
return b43_ntab_tx_gain_ipa_rev5_2g;
return b43_ntab_tx_gain_ipa_rev6_2g;
case 5:
return b43_ntab_tx_gain_ipa_rev5_2g;
case 4:
case 3:
return b43_ntab_tx_gain_ipa_rev3_2g;
default:
b43err(dev->wl,
"No 2GHz IPA gain table available for this device\n");
return NULL;
}
} else {
return txpwrctrl_tx_gain_ipa_5g;
switch (phy->rev) {
case 16:
if (phy->radio_rev == 9)
return b43_ntab_tx_gain_ipa_2057_rev9_5g;
case 3 ... 6:
return b43_ntab_tx_gain_ipa_rev3_5g;
default:
b43err(dev->wl,
"No 5GHz IPA gain table available for this device\n");
return NULL;
}
}
}
const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
enum ieee80211_band band = b43_current_band(dev->wl);
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@ -3152,19 +3523,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
(dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) {
return b43_nphy_get_ipa_gain_table(dev);
} else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
if (dev->phy.rev == 3)
return b43_ntab_tx_gain_rev3_5ghz;
if (dev->phy.rev == 4)
switch (phy->rev) {
case 6:
case 5:
return b43_ntab_tx_gain_epa_rev5_5g;
case 4:
return sprom->fem.ghz5.extpa_gain == 3 ?
b43_ntab_tx_gain_rev4_5ghz :
b43_ntab_tx_gain_rev4_5ghz; /* FIXME */
else
return b43_ntab_tx_gain_rev5plus_5ghz;
b43_ntab_tx_gain_epa_rev4_5g :
b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */
case 3:
return b43_ntab_tx_gain_epa_rev3_5g;
default:
b43err(dev->wl,
"No 5GHz EPA gain table available for this device\n");
return NULL;
}
} else {
if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3)
return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */
else
return b43_ntab_tx_gain_rev3plus_2ghz;
switch (phy->rev) {
case 6:
case 5:
if (sprom->fem.ghz5.extpa_gain == 3)
return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */
/* fall through */
case 4:
case 3:
return b43_ntab_tx_gain_epa_rev3_2g;
default:
b43err(dev->wl,
"No 2GHz EPA gain table available for this device\n");
return NULL;
}
}
}
@ -3191,7 +3579,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
/* Some workarounds to the workarounds... */
if (ghz5 && dev->phy.rev >= 6) {
if (dev->phy.radio_rev == 11 &&
!b43_channel_type_is_40mhz(dev->phy.channel_type))
!b43_is_40mhz(dev))
e->cliplo_gain = 0x2d;
} else if (!ghz5 && dev->phy.rev >= 5) {
static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a,

View file

@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
#define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */
#define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576)
/* Static N-PHY tables, PHY revision >= 7 */
#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */
#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */
#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18
#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18
#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18

View file

@ -34,7 +34,8 @@ brcmfmac-objs += \
dhd_common.o \
dhd_linux.o \
firmware.o \
btcoex.o
btcoex.o \
vendor.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
dhd_sdio.o \
bcmsdh.o

View file

@ -157,7 +157,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
*/
/* save current */
brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n");
brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
@ -165,7 +165,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
btci->saved_regs_part2 = true;
brcmf_dbg(TRACE,
brcmf_dbg(INFO,
"saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
btci->reg50, btci->reg51, btci->reg64,
btci->reg65, btci->reg71);
@ -179,21 +179,21 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
} else if (btci->saved_regs_part2) {
/* restore previously saved bt params */
brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n");
brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
brcmf_btcoex_params_write(ifp, 50, btci->reg50);
brcmf_btcoex_params_write(ifp, 51, btci->reg51);
brcmf_btcoex_params_write(ifp, 64, btci->reg64);
brcmf_btcoex_params_write(ifp, 65, btci->reg65);
brcmf_btcoex_params_write(ifp, 71, btci->reg71);
brcmf_dbg(TRACE,
brcmf_dbg(INFO,
"restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
btci->reg50, btci->reg51, btci->reg64,
btci->reg65, btci->reg71);
btci->saved_regs_part2 = false;
} else {
brcmf_err("attempted to restore not saved BTCOEX params\n");
brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
}
}
@ -219,14 +219,14 @@ static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
break;
}
brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27);
brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
if ((param27 & 0x6) == 2) { /* count both sco & esco */
sco_id_cnt++;
}
if (sco_id_cnt > 2) {
brcmf_dbg(TRACE,
brcmf_dbg(INFO,
"sco/esco detected, pkt id_cnt:%d samples:%d\n",
sco_id_cnt, i);
res = true;
@ -250,7 +250,7 @@ static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
btci->saved_regs_part1 = true;
brcmf_dbg(TRACE,
brcmf_dbg(INFO,
"saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
btci->reg66, btci->reg41,
btci->reg68);
@ -270,7 +270,7 @@ static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
brcmf_btcoex_params_write(ifp, 66, btci->reg66);
brcmf_btcoex_params_write(ifp, 41, btci->reg41);
brcmf_btcoex_params_write(ifp, 68, btci->reg68);
brcmf_dbg(TRACE,
brcmf_dbg(INFO,
"restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
btci->reg66, btci->reg41,
btci->reg68);
@ -307,7 +307,7 @@ static void brcmf_btcoex_handler(struct work_struct *work)
/* DHCP started provide OPPORTUNITY window
to get DHCP address
*/
brcmf_dbg(TRACE, "DHCP started\n");
brcmf_dbg(INFO, "DHCP started\n");
btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
mod_timer(&btci->timer, btci->timer.expires);
@ -322,12 +322,12 @@ static void brcmf_btcoex_handler(struct work_struct *work)
case BRCMF_BT_DHCP_OPPR_WIN:
if (btci->dhcp_done) {
brcmf_dbg(TRACE, "DHCP done before T1 expiration\n");
brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
goto idle;
}
/* DHCP is not over yet, start lowering BT priority */
brcmf_dbg(TRACE, "DHCP T1:%d expired\n",
brcmf_dbg(INFO, "DHCP T1:%d expired\n",
BRCMF_BTCOEX_OPPR_WIN_TIME);
brcmf_btcoex_boost_wifi(btci, true);
@ -339,9 +339,9 @@ static void brcmf_btcoex_handler(struct work_struct *work)
case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
if (btci->dhcp_done)
brcmf_dbg(TRACE, "DHCP done before T2 expiration\n");
brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
else
brcmf_dbg(TRACE, "DHCP T2:%d expired\n",
brcmf_dbg(INFO, "DHCP T2:%d expired\n",
BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
goto idle;
@ -440,13 +440,13 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
/* Stop any bt timer because DHCP session is done */
btci->dhcp_done = true;
if (btci->timer_on) {
brcmf_dbg(TRACE, "disable BT DHCP Timer\n");
brcmf_dbg(INFO, "disable BT DHCP Timer\n");
btci->timer_on = false;
del_timer_sync(&btci->timer);
/* schedule worker if transition to IDLE is needed */
if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
brcmf_dbg(TRACE, "bt_state:%d\n",
brcmf_dbg(INFO, "bt_state:%d\n",
btci->bt_state);
schedule_work(&btci->work);
}
@ -472,7 +472,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
switch (mode) {
case BRCMF_BTCOEX_DISABLED:
brcmf_dbg(TRACE, "DHCP session starts\n");
brcmf_dbg(INFO, "DHCP session starts\n");
if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
return -EBUSY;
/* Start BT timer only for SCO connection */
@ -484,14 +484,14 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
break;
case BRCMF_BTCOEX_ENABLED:
brcmf_dbg(TRACE, "DHCP session ends\n");
brcmf_dbg(INFO, "DHCP session ends\n");
if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
vif == btci->vif) {
brcmf_btcoex_dhcp_end(btci);
}
break;
default:
brcmf_dbg(TRACE, "Unknown mode, ignored\n");
brcmf_dbg(INFO, "Unknown mode, ignored\n");
}
return 0;
}

View file

@ -49,16 +49,6 @@
*/
#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
/* Bus independent dongle command */
struct brcmf_dcmd {
uint cmd; /* common dongle cmd definition */
void *buf; /* pointer to user buffer */
uint len; /* length of user buffer */
u8 set; /* get or set request (optional) */
uint used; /* bytes read or written (optional) */
uint needed; /* bytes needed (optional) */
};
/**
* struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
*

View file

@ -282,6 +282,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
ptr = strrchr(buf, ' ') + 1;
strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
/* set mpc */
err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
if (err) {
brcmf_err("failed setting mpc\n");
goto done;
}
/*
* Setup timeout if Beacons are lost and roam is off to report
* link down

View file

@ -54,7 +54,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
if (err >= 0)
err = 0;
else
brcmf_err("Failed err=%d\n", err);
brcmf_dbg(FIL, "Failed err=%d\n", err);
return err;
}

View file

@ -708,7 +708,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
else if (num_chans == AF_PEER_SEARCH_CNT)
active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
active = -1;
else
active = P2PAPI_SCAN_DWELL_TIME_MS;

View file

@ -29,32 +29,24 @@
#include "usb_rdl.h"
#include "usb.h"
#define IOCTL_RESP_TIMEOUT 2000
#define IOCTL_RESP_TIMEOUT 2000
#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
has boot up */
#define BRCMF_USB_NRXQ 50
#define BRCMF_USB_NTXQ 50
#define BRCMF_USB_NRXQ 50
#define BRCMF_USB_NTXQ 50
#define CONFIGDESC(usb) (&((usb)->actconfig)->desc)
#define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)])
#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0])
#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc
#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc
#define BRCMF_USB_CBCTL_WRITE 0
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
#define CONTROL_IF 0
#define BULK_IF 0
#define BRCMF_USB_CBCTL_WRITE 0
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
struct brcmf_usb_image {
struct list_head list;
@ -70,7 +62,7 @@ struct brcmf_usbdev_info {
struct list_head rx_postq;
struct list_head tx_freeq;
struct list_head tx_postq;
uint rx_pipe, tx_pipe, rx_pipe2;
uint rx_pipe, tx_pipe;
int rx_low_watermark;
int tx_low_watermark;
@ -97,6 +89,7 @@ struct brcmf_usbdev_info {
int ctl_completed;
wait_queue_head_t ioctl_resp_wait;
ulong ctl_op;
u8 ifnum;
struct urb *bulk_urb; /* used for FW download */
};
@ -576,7 +569,6 @@ fail:
static int brcmf_usb_up(struct device *dev)
{
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
u16 ifnum;
brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@ -589,21 +581,19 @@ static int brcmf_usb_up(struct device *dev)
devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber;
/* CTL Write */
devinfo->ctl_write.bRequestType =
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_write.bRequest = 0;
devinfo->ctl_write.wValue = cpu_to_le16(0);
devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum);
devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
/* CTL Read */
devinfo->ctl_read.bRequestType =
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_read.bRequest = 1;
devinfo->ctl_read.wValue = cpu_to_le16(0);
devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum);
devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
}
brcmf_usb_rx_fill_all(devinfo);
return 0;
@ -642,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb)
brcmf_usb_ioctl_resp_wake(devinfo);
}
static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
void *buffer, int buflen)
static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
void *buffer, int buflen)
{
int ret = 0;
int ret;
char *tmpbuf;
u16 size;
if ((!devinfo) || (devinfo->ctl_urb == NULL))
return false;
return -EINVAL;
tmpbuf = kmalloc(buflen, GFP_ATOMIC);
if (!tmpbuf)
return false;
return -ENOMEM;
size = buflen;
devinfo->ctl_urb->transfer_buffer_length = size;
@ -675,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
if (ret < 0) {
brcmf_err("usb_submit_urb failed %d\n", ret);
kfree(tmpbuf);
return false;
goto finalize;
}
ret = brcmf_usb_ioctl_resp_wait(devinfo);
memcpy(buffer, tmpbuf, buflen);
kfree(tmpbuf);
if (!brcmf_usb_ioctl_resp_wait(devinfo))
ret = -ETIMEDOUT;
else
memcpy(buffer, tmpbuf, buflen);
finalize:
kfree(tmpbuf);
return ret;
}
@ -724,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
{
struct bootrom_id_le id;
u32 loop_cnt;
int err;
brcmf_dbg(USB, "Enter\n");
@ -732,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
loop_cnt++;
id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
if ((err) && (err != -ETIMEDOUT))
return err;
if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
break;
} while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
@ -794,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
}
/* 1) Prepare USB boot loader for runtime image */
brcmf_usb_dl_cmd(devinfo, DL_START, &state,
sizeof(struct rdl_state_le));
brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
rdlstate = le32_to_cpu(state.state);
rdlbytes = le32_to_cpu(state.bytes);
@ -839,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
dlpos += sendlen;
sent += sendlen;
}
if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
sizeof(struct rdl_state_le))) {
brcmf_err("DL_GETSTATE Failed xxxx\n");
err = -EINVAL;
err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
sizeof(state));
if (err) {
brcmf_err("DL_GETSTATE Failed\n");
goto fail;
}
@ -898,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
return -EINVAL;
/* Check we are runnable */
brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
sizeof(struct rdl_state_le));
state.state = 0;
brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
/* Start the image */
if (state.state == cpu_to_le32(DL_RUNNABLE)) {
if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state,
sizeof(struct rdl_state_le)))
if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
return -ENODEV;
if (brcmf_usb_resetcfg(devinfo))
return -ENODEV;
@ -928,6 +921,9 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev)
return (chiprev == 3);
case 43242:
return true;
case 43566:
case 43569:
return true;
default:
break;
}
@ -1028,6 +1024,9 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
return BRCMF_USB_43236_FW_NAME;
case 43242:
return BRCMF_USB_43242_FW_NAME;
case 43566:
case 43569:
return BRCMF_USB_43569_FW_NAME;
default:
return NULL;
}
@ -1221,15 +1220,15 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
static int
brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
int ep;
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo;
struct usb_interface_descriptor *desc;
struct usb_endpoint_descriptor *endpoint;
int ret = 0;
struct usb_device *usb = interface_to_usbdev(intf);
int num_of_eps;
u8 endpoint_num;
struct brcmf_usbdev_info *devinfo;
u32 num_of_eps;
u8 endpoint_num, ep;
brcmf_dbg(USB, "Enter\n");
brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
if (devinfo == NULL)
@ -1237,92 +1236,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->usbdev = usb;
devinfo->dev = &usb->dev;
usb_set_intfdata(intf, devinfo);
/* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) {
ret = -1;
brcmf_err("Number of configurations: %d not supported\n",
usb->descriptor.bNumConfigurations);
ret = -ENODEV;
goto fail;
}
if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
ret = -1;
if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
(usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
(usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
brcmf_err("Device class: 0x%x not supported\n",
usb->descriptor.bDeviceClass);
ret = -ENODEV;
goto fail;
}
/*
* Only the BDC interface configuration is supported:
* Device class: USB_CLASS_VENDOR_SPEC
* if0 class: USB_CLASS_VENDOR_SPEC
* if0/ep0: control
* if0/ep1: bulk in
* if0/ep2: bulk out (ok if swapped with bulk in)
*/
if (CONFIGDESC(usb)->bNumInterfaces != 1) {
ret = -1;
desc = &intf->altsetting[0].desc;
if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
(desc->bInterfaceSubClass != 2) ||
(desc->bInterfaceProtocol != 0xff)) {
brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
desc->bInterfaceNumber, desc->bInterfaceClass,
desc->bInterfaceSubClass, desc->bInterfaceProtocol);
ret = -ENODEV;
goto fail;
}
/* Check interface */
if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 ||
IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) {
brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n",
IFDESC(usb, CONTROL_IF).bInterfaceClass,
IFDESC(usb, CONTROL_IF).bInterfaceSubClass,
IFDESC(usb, CONTROL_IF).bInterfaceProtocol);
ret = -1;
goto fail;
}
/* Check control endpoint */
endpoint = &IFEPDESC(usb, CONTROL_IF, 0);
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_INT) {
brcmf_err("invalid control endpoint %d\n",
endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
ret = -1;
goto fail;
}
devinfo->rx_pipe = 0;
devinfo->rx_pipe2 = 0;
devinfo->tx_pipe = 0;
num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1;
/* Check data endpoints and get pipes */
for (ep = 1; ep <= num_of_eps; ep++) {
endpoint = &IFEPDESC(usb, BULK_IF, ep);
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_BULK) {
brcmf_err("invalid data endpoint %d\n", ep);
ret = -1;
goto fail;
}
endpoint_num = endpoint->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
== USB_DIR_IN) {
if (!devinfo->rx_pipe) {
num_of_eps = desc->bNumEndpoints;
for (ep = 0; ep < num_of_eps; ep++) {
endpoint = &intf->altsetting[0].endpoint[ep].desc;
endpoint_num = usb_endpoint_num(endpoint);
if (!usb_endpoint_xfer_bulk(endpoint))
continue;
if (usb_endpoint_dir_in(endpoint)) {
if (!devinfo->rx_pipe)
devinfo->rx_pipe =
usb_rcvbulkpipe(usb, endpoint_num);
} else {
devinfo->rx_pipe2 =
usb_rcvbulkpipe(usb, endpoint_num);
}
} else {
devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num);
if (!devinfo->tx_pipe)
devinfo->tx_pipe =
usb_sndbulkpipe(usb, endpoint_num);
}
}
if (devinfo->rx_pipe == 0) {
brcmf_err("No RX (in) Bulk EP found\n");
ret = -ENODEV;
goto fail;
}
if (devinfo->tx_pipe == 0) {
brcmf_err("No TX (out) Bulk EP found\n");
ret = -ENODEV;
goto fail;
}
devinfo->ifnum = desc->bInterfaceNumber;
if (usb->speed == USB_SPEED_SUPER)
brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n");
brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
else if (usb->speed == USB_SPEED_HIGH)
brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n");
brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
else
brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n");
brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
ret = brcmf_usb_probe_cb(devinfo);
if (ret)
@ -1332,11 +1310,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
return 0;
fail:
brcmf_err("failed with errno %d\n", ret);
kfree(devinfo);
usb_set_intfdata(intf, NULL);
return ret;
}
static void
@ -1381,6 +1357,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n");
return brcmf_fw_get_firmwares(&usb->dev, 0,
@ -1392,12 +1369,14 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
#define BRCMF_USB_DEVICE_ID_43143 0xbd1e
#define BRCMF_USB_DEVICE_ID_43236 0xbd17
#define BRCMF_USB_DEVICE_ID_43242 0xbd1f
#define BRCMF_USB_DEVICE_ID_43569 0xbd27
#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc
static struct usb_device_id brcmf_usb_devid_table[] = {
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) },
/* special entry for device with firmware loaded and running */
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
{ }
@ -1407,6 +1386,7 @@ MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME);
static struct usb_driver brcmf_usbdrvr = {
.name = KBUILD_MODNAME,

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2014 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/vmalloc.h>
#include <net/cfg80211.h>
#include <net/netlink.h>
#include <brcmu_wifi.h>
#include "fwil_types.h"
#include "dhd.h"
#include "p2p.h"
#include "dhd_dbg.h"
#include "wl_cfg80211.h"
#include "vendor.h"
#include "fwil.h"
static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int len)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = cfg_to_ndev(cfg);
const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
struct sk_buff *reply;
int ret, payload, ret_len;
void *dcmd_buf = NULL, *wr_pointer;
u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
cmdhdr->len);
len -= sizeof(struct brcmf_vndr_dcmd_hdr);
ret_len = cmdhdr->len;
if (ret_len > 0 || len > 0) {
if (len > BRCMF_DCMD_MAXLEN) {
brcmf_err("oversize input buffer %d\n", len);
len = BRCMF_DCMD_MAXLEN;
}
if (ret_len > BRCMF_DCMD_MAXLEN) {
brcmf_err("oversize return buffer %d\n", ret_len);
ret_len = BRCMF_DCMD_MAXLEN;
}
payload = max(ret_len, len) + 1;
dcmd_buf = vzalloc(payload);
if (NULL == dcmd_buf)
return -ENOMEM;
memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len);
*(char *)(dcmd_buf + len) = '\0';
}
if (cmdhdr->set)
ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
dcmd_buf, ret_len);
else
ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
dcmd_buf, ret_len);
if (ret != 0)
goto exit;
wr_pointer = dcmd_buf;
while (ret_len > 0) {
msglen = ret_len > maxmsglen ? maxmsglen : ret_len;
ret_len -= msglen;
payload = msglen + sizeof(msglen);
reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
if (NULL == reply) {
ret = -ENOMEM;
break;
}
if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) ||
nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) {
kfree_skb(reply);
ret = -ENOBUFS;
break;
}
ret = cfg80211_vendor_cmd_reply(reply);
if (ret)
break;
wr_pointer += msglen;
}
exit:
vfree(dcmd_buf);
return ret;
}
const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
{
{
.vendor_id = BROADCOM_OUI,
.subcmd = BRCMF_VNDR_CMDS_DCMD
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
},
};

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2014 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _vendor_h_
#define _vendor_h_
#define BROADCOM_OUI 0x001018
enum brcmf_vndr_cmds {
BRCMF_VNDR_CMDS_UNSPEC,
BRCMF_VNDR_CMDS_DCMD,
BRCMF_VNDR_CMDS_LAST
};
/**
* enum brcmf_nlattrs - nl80211 message attributes
*
* @BRCMF_NLATTR_LEN: message body length
* @BRCMF_NLATTR_DATA: message body
*/
enum brcmf_nlattrs {
BRCMF_NLATTR_UNSPEC,
BRCMF_NLATTR_LEN,
BRCMF_NLATTR_DATA,
__BRCMF_NLATTR_AFTER_LAST,
BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1
};
/**
* struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd
* support
*
* @cmd: common dongle cmd definition
* @len: length of expecting return buffer
* @offset: offset of data buffer
* @set: get or set request(optional)
* @magic: magic number for verification
*/
struct brcmf_vndr_dcmd_hdr {
uint cmd;
int len;
uint offset;
uint set;
uint magic;
};
extern const struct wiphy_vendor_command brcmf_vendor_cmds[];
#endif /* _vendor_h_ */

View file

@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <net/cfg80211.h>
#include <net/netlink.h>
@ -33,6 +34,7 @@
#include "btcoex.h"
#include "wl_cfg80211.h"
#include "fwil.h"
#include "vendor.h"
#define BRCMF_SCAN_IE_LEN_MAX 2048
#define BRCMF_PNO_VERSION 2
@ -588,6 +590,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
}
}
static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
{
if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329)
brcmf_set_mpc(ifp, mpc);
}
void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
{
s32 err = 0;
@ -641,7 +649,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
brcmf_err("Scan abort failed\n");
}
brcmf_set_mpc(ifp, 1);
brcmf_scan_config_mpc(ifp, 1);
/*
* e-scan can be initiated by scheduled scan
@ -920,7 +928,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
brcmf_err("error (%d)\n", err);
return err;
}
brcmf_set_mpc(ifp, 0);
brcmf_scan_config_mpc(ifp, 0);
results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
results->version = 0;
results->count = 0;
@ -928,7 +936,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
if (err)
brcmf_set_mpc(ifp, 1);
brcmf_scan_config_mpc(ifp, 1);
return err;
}
@ -1019,7 +1027,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
goto scan_out;
}
brcmf_set_mpc(ifp, 0);
brcmf_scan_config_mpc(ifp, 0);
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
&sr->ssid_le, sizeof(sr->ssid_le));
if (err) {
@ -1029,7 +1037,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
else
brcmf_err("WLC_SCAN error (%d)\n", err);
brcmf_set_mpc(ifp, 1);
brcmf_scan_config_mpc(ifp, 1);
goto scan_out;
}
}
@ -1331,7 +1339,6 @@ static s32
brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@ -1341,7 +1348,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
brcmf_dbg(TRACE, "Exit\n");
return err;
return 0;
}
static s32 brcmf_set_wpa_version(struct net_device *ndev,
@ -2388,7 +2395,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
struct cfg80211_bss *bss;
struct ieee80211_supported_band *band;
struct brcmu_chan ch;
s32 err = 0;
u16 channel;
u32 freq;
u16 notify_capability;
@ -2438,7 +2444,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
cfg80211_put_bss(wiphy, bss);
return err;
return 0;
}
static struct brcmf_bss_info_le *
@ -2690,7 +2696,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
s32 status;
s32 err = 0;
struct brcmf_escan_result_le *escan_result_le;
struct brcmf_bss_info_le *bss_info_le;
struct brcmf_bss_info_le *bss = NULL;
@ -2781,7 +2786,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
status);
}
exit:
return err;
return 0;
}
static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
@ -3260,35 +3265,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
return 0;
}
#ifdef CONFIG_NL80211_TESTMODE
static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
struct wireless_dev *wdev,
void *data, int len)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = cfg_to_ndev(cfg);
struct brcmf_dcmd *dcmd = data;
struct sk_buff *reply;
int ret;
brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
dcmd->buf, dcmd->len);
if (dcmd->set)
ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
dcmd->buf, dcmd->len);
else
ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
dcmd->buf, dcmd->len);
if (ret == 0) {
reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
ret = cfg80211_testmode_reply(reply);
}
return ret;
}
#endif
static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
{
s32 err;
@ -3507,7 +3483,6 @@ static s32
brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
struct parsed_vndr_ies *vndr_ies)
{
s32 err = 0;
struct brcmf_vs_tlv *vndrie;
struct brcmf_tlv *ie;
struct parsed_vndr_ie_info *parsed_info;
@ -3560,7 +3535,7 @@ next:
ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
TLV_HDR_LEN);
}
return err;
return 0;
}
static u32
@ -4307,7 +4282,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.crit_proto_start = brcmf_cfg80211_crit_proto_start,
.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
.tdls_oper = brcmf_cfg80211_tdls_oper,
CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
};
static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
@ -4412,6 +4386,11 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
brcmf_dbg(INFO, "Registering custom regulatory\n");
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
/* vendor commands/events support */
wiphy->vendor_commands = brcmf_vendor_cmds;
wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
err = wiphy_register(wiphy);
if (err < 0) {
brcmf_err("Could not register wiphy device (%d)\n", err);
@ -4650,7 +4629,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
@ -4676,7 +4654,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
completed ? "succeeded" : "failed");
}
brcmf_dbg(TRACE, "Exit\n");
return err;
return 0;
}
static s32
@ -4768,7 +4746,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
const struct brcmf_event_msg *e, void *data)
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
s32 err = 0;
u32 event = e->event_code;
u32 status = e->status;
@ -4779,7 +4756,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
}
return err;
return 0;
}
static s32
@ -5057,6 +5034,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
BRCMF_OBSS_COEX_AUTO);
}
/* clear for now and rely on update later */
wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false;
wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0;
err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
if (err) {
@ -5625,16 +5605,15 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
return wdev->iftype;
}
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state)
{
struct brcmf_cfg80211_vif *vif;
bool result = 0;
list_for_each_entry(vif, &cfg->vif_list, list) {
if (test_bit(state, &vif->sme_state))
result++;
return true;
}
return result;
return false;
}
static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,

View file

@ -477,7 +477,7 @@ const struct brcmf_tlv *
brcmf_parse_tlvs(const void *buf, int buflen, uint key);
u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
struct ieee80211_channel *ch);
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state);
void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif);
bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);

View file

@ -1538,11 +1538,7 @@ static s8
wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band,
u8 rate)
{
s8 offset = 0;
if (!pi->user_txpwr_at_rfport)
return offset;
return offset;
return 0;
}
void wlc_phy_txpower_recalc_target(struct brcms_phy *pi)

View file

@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct cw1200_common *priv = hw->priv;
struct cfg80211_scan_request *req = &hw_req->req;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};

View file

@ -41,7 +41,7 @@ struct cw1200_scan {
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
struct ieee80211_scan_request *hw_req);
void cw1200_scan_work(struct work_struct *work);
void cw1200_scan_timeout(struct work_struct *work);
void cw1200_clear_recent_scan_work(struct work_struct *work);

View file

@ -2289,7 +2289,6 @@ static int cw1200_upload_null(struct cw1200_common *priv)
static int cw1200_upload_qosnull(struct cw1200_common *priv)
{
int ret = 0;
/* TODO: This needs to be implemented
struct wsm_template_frame frame = {
@ -2306,7 +2305,7 @@ static int cw1200_upload_qosnull(struct cw1200_common *priv)
dev_kfree_skb(frame.skb);
*/
return ret;
return 0;
}
static int cw1200_enable_beaconing(struct cw1200_common *priv,

View file

@ -100,8 +100,7 @@ static inline void libipw_networks_free(struct libipw_device *ieee)
int i;
for (i = 0; i < MAX_NETWORK_COUNT; i++) {
if (ieee->networks[i]->ibss_dfs)
kfree(ieee->networks[i]->ibss_dfs);
kfree(ieee->networks[i]->ibss_dfs);
kfree(ieee->networks[i]);
}
}

View file

@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
int
il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct cfg80211_scan_request *req = &hw_req->req;
struct il_priv *il = hw->priv;
int ret;

View file

@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il);
int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
void il_force_scan_end(struct il_priv *il);
int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
struct ieee80211_scan_request *hw_req);
void il_internal_short_hw_scan(struct il_priv *il);
int il_force_reset(struct il_priv *il, bool external);
u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,

View file

@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
struct cfg80211_scan_request *req = &hw_req->req;
int ret;
IWL_DEBUG_MAC80211(priv, "enter\n");

View file

@ -40,6 +40,10 @@
#include "commands.h"
#include "power.h"
static bool force_cam;
module_param(force_cam, bool, 0644);
MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
/*
* Setting power level allows the card to go to sleep when not busy.
*
@ -288,6 +292,11 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
int dtimper;
if (force_cam) {
iwl_power_sleep_cam_cmd(priv, cmd);
return;
}
dtimper = priv->hw->conf.ps_dtim_period ?: 1;
if (priv->wowlan)

View file

@ -1068,13 +1068,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
/* recalculate basic rates */
iwl_calc_basic_rates(priv, ctx);
/*
* force CTS-to-self frames protection if RTS-CTS is not preferred
* one aggregation protection method
*/
if (!priv->hw_params.use_rts_for_aggregation)
ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
@ -1480,11 +1473,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
else
ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
if (bss_conf->use_cts_prot)
ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
else
ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
if (vif->type == NL80211_IFTYPE_AP ||

View file

@ -67,7 +67,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL8000_UCODE_API_MAX 8
#define IWL8000_UCODE_API_MAX 9
/* Oldest version we won't warn about */
#define IWL8000_UCODE_API_OK 8
@ -119,10 +119,9 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
.default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
};
const struct iwl_cfg iwl8260_n_cfg = {
const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
.name = "Intel(R) Dual Band Wireless-AC 8260",
.fw_name_pre = IWL8000_FW_PRE,
IWL_DEVICE_8000,

View file

@ -337,7 +337,7 @@ extern const struct iwl_cfg iwl7265_2ac_cfg;
extern const struct iwl_cfg iwl7265_2n_cfg;
extern const struct iwl_cfg iwl7265_n_cfg;
extern const struct iwl_cfg iwl8260_2ac_cfg;
extern const struct iwl_cfg iwl8260_n_cfg;
extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */

View file

@ -155,6 +155,8 @@ static struct iwlwifi_opmode_table {
[MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
};
#define IWL_DEFAULT_SCAN_CHANNELS 40
/*
* struct fw_sec: Just for the image parsing proccess.
* For the fw storage we are using struct fw_desc.
@ -565,6 +567,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
}
drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
memcpy(drv->fw.human_readable, ucode->human_readable,
sizeof(drv->fw.human_readable));
build = le32_to_cpu(ucode->build);
if (build)
@ -819,6 +823,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
goto invalid_tlv_len;
break;
case IWL_UCODE_TLV_N_SCAN_CHANNELS:
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
capa->n_scan_channels =
le32_to_cpup((__le32 *)tlv_data);
break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
@ -973,6 +983,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
if (!api_ok)
api_ok = api_max;
@ -1394,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level,
int, S_IRUGO);
MODULE_PARM_DESC(power_level,
"default power save level (range from 1 - 5, default: 1)");
module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
MODULE_PARM_DESC(fw_monitor,
"firmware monitor - to debug FW (default: false - needs lots of memory)");

View file

@ -779,7 +779,6 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
if (cfg->ht_params->ht40_bands & BIT(band)) {
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
ht_info->mcs.rx_mask[4] = 0x01;
max_bit_rate = MAX_BIT_RATE_40_MHZ;
}

View file

@ -74,12 +74,17 @@
* @IWL_FW_ERROR_DUMP_RXF:
* @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
* &struct iwl_fw_error_dump_txcmd packets
* @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
* info on the device / firmware.
* @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
*/
enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_SRAM = 0,
IWL_FW_ERROR_DUMP_REG = 1,
IWL_FW_ERROR_DUMP_RXF = 2,
IWL_FW_ERROR_DUMP_TXCMD = 3,
IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
IWL_FW_ERROR_DUMP_MAX,
};
@ -87,8 +92,8 @@ enum iwl_fw_error_dump_type {
/**
* struct iwl_fw_error_dump_data - data for one type
* @type: %enum iwl_fw_error_dump_type
* @len: the length starting from %data - must be a multiplier of 4.
* @data: the data itself padded to be a multiplier of 4.
* @len: the length starting from %data
* @data: the data itself
*/
struct iwl_fw_error_dump_data {
__le32 type;
@ -120,13 +125,50 @@ struct iwl_fw_error_dump_txcmd {
u8 data[];
} __packed;
enum iwl_fw_error_dump_family {
IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
};
/**
* iwl_mvm_fw_error_next_data - advance fw error dump data pointer
* struct iwl_fw_error_dump_info - info on the device / firmware
* @device_family: the family of the device (7 / 8)
* @hw_step: the step of the device
* @fw_human_readable: human readable FW version
* @dev_human_readable: name of the device
* @bus_human_readable: name of the bus used
*/
struct iwl_fw_error_dump_info {
__le32 device_family;
__le32 hw_step;
u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
u8 dev_human_readable[64];
u8 bus_human_readable[8];
} __packed;
/**
* struct iwl_fw_error_dump_fw_mon - FW monitor data
* @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
* @fw_mon_base_ptr: base pointer of the data
* @fw_mon_cycle_cnt: number of wrap arounds
* @reserved: for future use
* @data: captured data
*/
struct iwl_fw_error_dump_fw_mon {
__le32 fw_mon_wr_ptr;
__le32 fw_mon_base_ptr;
__le32 fw_mon_cycle_cnt;
__le32 reserved[3];
u8 data[];
} __packed;
/**
* iwl_fw_error_next_data - advance fw error dump data pointer
* @data: previous data block
* Returns: next data block
*/
static inline struct iwl_fw_error_dump_data *
iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
{
return (void *)(data->data + le32_to_cpu(data->len));
}

View file

@ -128,6 +128,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_CSCHEME = 28,
IWL_UCODE_TLV_API_CHANGES_SET = 29,
IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
};
struct iwl_ucode_tlv {
@ -136,7 +137,8 @@ struct iwl_ucode_tlv {
u8 data[0];
};
#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
#define FW_VER_HUMAN_READABLE_SZ 64
struct iwl_tlv_ucode_header {
/*
@ -147,7 +149,7 @@ struct iwl_tlv_ucode_header {
*/
__le32 zero;
__le32 magic;
u8 human_readable[64];
u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
__le32 ver; /* major/minor/API/serial */
__le32 build;
__le64 ignore;

View file

@ -65,6 +65,8 @@
#include <linux/types.h>
#include <net/mac80211.h>
#include "iwl-fw-file.h"
/**
* enum iwl_ucode_tlv_flag - ucode API flags
* @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
@ -88,6 +90,7 @@
* P2P client interfaces simultaneously if they are in different bindings.
* @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
* P2P client interfaces simultaneously if they are in same bindings.
* @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
* @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
* @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
* @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
@ -117,11 +120,19 @@ enum iwl_ucode_tlv_flag {
/**
* enum iwl_ucode_tlv_api - ucode api
* @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
* @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
* @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
* @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
* @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
* @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
*/
enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1),
IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6),
};
/**
@ -178,6 +189,7 @@ enum iwl_ucode_sec {
struct iwl_ucode_capabilities {
u32 max_probe_length;
u32 n_scan_channels;
u32 standard_phy_calibration_size;
u32 flags;
u32 api[IWL_API_ARRAY_SIZE];
@ -311,6 +323,7 @@ struct iwl_fw {
bool mvm_fw;
struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
};
#endif /* __iwl_fw_h__ */

View file

@ -103,6 +103,7 @@ enum iwl_disable_11n {
* @power_level: power level, default = 1
* @debug_level: levels are IWL_DL_*
* @ant_coupling: antenna coupling in dB, default = 0
* @fw_monitor: allow to use firmware monitor
*/
struct iwl_mod_params {
int sw_crypto;
@ -120,6 +121,7 @@ struct iwl_mod_params {
int ant_coupling;
char *nvm_file;
bool uapsd_disable;
bool fw_monitor;
};
#endif /* #__iwl_modparams_h__ */

View file

@ -63,6 +63,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "iwl-drv.h"
#include "iwl-modparams.h"
#include "iwl-nvm-parse.h"
@ -87,8 +88,10 @@ enum wkp_nvm_offsets {
enum family_8000_nvm_offsets {
/* NVM HW-Section offset (in words) definitions */
HW_ADDR0_FAMILY_8000 = 0x12,
HW_ADDR1_FAMILY_8000 = 0x16,
HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
/* NVM SW-Section offset (in words) definitions */
@ -174,7 +177,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
* @NVM_CHANNEL_IBSS: usable as an IBSS channel
* @NVM_CHANNEL_ACTIVE: active scanning allowed
* @NVM_CHANNEL_RADAR: radar detection required
* @NVM_CHANNEL_DFS: dynamic freq selection candidate
* @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
* @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
* on same channel on 2.4 or same UNII band on 5.2
* @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
* @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
* @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
@ -185,7 +190,8 @@ enum iwl_nvm_channel_flags {
NVM_CHANNEL_IBSS = BIT(1),
NVM_CHANNEL_ACTIVE = BIT(3),
NVM_CHANNEL_RADAR = BIT(4),
NVM_CHANNEL_DFS = BIT(7),
NVM_CHANNEL_INDOOR_ONLY = BIT(5),
NVM_CHANNEL_GO_CONCURRENT = BIT(6),
NVM_CHANNEL_WIDE = BIT(8),
NVM_CHANNEL_40MHZ = BIT(9),
NVM_CHANNEL_80MHZ = BIT(10),
@ -273,6 +279,16 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
if (ch_flags & NVM_CHANNEL_RADAR)
channel->flags |= IEEE80211_CHAN_RADAR;
if (ch_flags & NVM_CHANNEL_INDOOR_ONLY)
channel->flags |= IEEE80211_CHAN_INDOOR_ONLY;
/* Set the GO concurrent flag only in case that NO_IR is set.
* Otherwise it is meaningless
*/
if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) &&
(channel->flags & IEEE80211_CHAN_NO_IR))
channel->flags |= IEEE80211_CHAN_GO_CONCURRENT;
/* Initialize regulatory-based run-time data */
/*
@ -282,7 +298,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
channel->max_power = DEFAULT_MAX_TX_POWER;
is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
IWL_DEBUG_EEPROM(dev,
"Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
"Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
channel->hw_value,
is_5ghz ? "5.2" : "2.4",
CHECK_AND_PRINT_I(VALID),
@ -290,7 +306,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
CHECK_AND_PRINT_I(ACTIVE),
CHECK_AND_PRINT_I(RADAR),
CHECK_AND_PRINT_I(WIDE),
CHECK_AND_PRINT_I(DFS),
CHECK_AND_PRINT_I(INDOOR_ONLY),
CHECK_AND_PRINT_I(GO_CONCURRENT),
ch_flags,
channel->max_power,
((ch_flags & NVM_CHANNEL_IBSS) &&
@ -462,7 +479,8 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
data->hw_addr[5] = hw_addr[4];
}
static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
static void iwl_set_hw_address_family_8000(struct device *dev,
const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
const __le16 *nvm_hw)
@ -481,20 +499,64 @@ static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
data->hw_addr[4] = hw_addr[5];
data->hw_addr[5] = hw_addr[4];
if (is_valid_ether_addr(hw_addr))
if (is_valid_ether_addr(data->hw_addr))
return;
IWL_ERR_DEV(dev,
"mac address from nvm override section is not valid\n");
}
/* take the MAC address from the OTP */
hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
data->hw_addr[0] = hw_addr[3];
data->hw_addr[1] = hw_addr[2];
data->hw_addr[2] = hw_addr[1];
data->hw_addr[3] = hw_addr[0];
if (nvm_hw) {
/* read the MAC address from OTP */
if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
/* read the mac address from the WFPM location */
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR0_WFPM_FAMILY_8000);
data->hw_addr[0] = hw_addr[3];
data->hw_addr[1] = hw_addr[2];
data->hw_addr[2] = hw_addr[1];
data->hw_addr[3] = hw_addr[0];
hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
data->hw_addr[4] = hw_addr[1];
data->hw_addr[5] = hw_addr[0];
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR1_WFPM_FAMILY_8000);
data->hw_addr[4] = hw_addr[1];
data->hw_addr[5] = hw_addr[0];
} else if ((data->nvm_version >= 0xE08) &&
(data->nvm_version < 0xE0B)) {
/* read "reverse order" from the PCIe location */
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR0_PCIE_FAMILY_8000);
data->hw_addr[5] = hw_addr[2];
data->hw_addr[4] = hw_addr[1];
data->hw_addr[3] = hw_addr[0];
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR1_PCIE_FAMILY_8000);
data->hw_addr[2] = hw_addr[3];
data->hw_addr[1] = hw_addr[2];
data->hw_addr[0] = hw_addr[1];
} else {
/* read from the PCIe location */
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR0_PCIE_FAMILY_8000);
data->hw_addr[5] = hw_addr[0];
data->hw_addr[4] = hw_addr[1];
data->hw_addr[3] = hw_addr[2];
hw_addr = (const u8 *)(nvm_hw +
HW_ADDR1_PCIE_FAMILY_8000);
data->hw_addr[2] = hw_addr[1];
data->hw_addr[1] = hw_addr[2];
data->hw_addr[0] = hw_addr[3];
}
if (!is_valid_ether_addr(data->hw_addr))
IWL_ERR_DEV(dev,
"mac address from hw section is not valid\n");
return;
}
IWL_ERR_DEV(dev, "mac address is not found\n");
}
struct iwl_nvm_data *
@ -556,7 +618,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
rx_chains);
} else {
/* MAC address in family 8000 */
iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
nvm_hw);
iwl_init_sbands(dev, cfg, data, regulatory,
sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,

View file

@ -359,4 +359,10 @@ enum secure_load_status_reg {
#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10)
#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c)
/* FW monitor */
#define MON_BUFF_BASE_ADDR (0xa03c3c)
#define MON_BUFF_END_ADDR (0xa03c40)
#define MON_BUFF_WRPTR (0xa03c44)
#define MON_BUFF_CYCLE_CNT (0xa03c48)
#endif /* __iwl_prph_h__ */

View file

@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o
iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o coex.o
iwlmvm-y += power.o coex.o coex_legacy.o
iwlmvm-y += tt.o offloading.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o

View file

@ -70,47 +70,8 @@
#include "mvm.h"
#include "iwl-debug.h"
#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
[(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
BT_COEX_PRIO_TBL_PRIO_LOW, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
BT_COEX_PRIO_TBL_PRIO_LOW, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
BT_COEX_PRIO_TBL_DISABLED, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
0, 0, 0, 0, 0, 0,
};
#undef EVENT_PRIO_ANT
#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
#define BT_ANTENNA_COUPLING_THRESHOLD (30)
static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
{
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
sizeof(struct iwl_bt_coex_prio_tbl_cmd),
&iwl_bt_prio_tbl);
}
const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
@ -519,6 +480,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
struct ieee80211_chanctx_conf *chanctx_conf;
enum iwl_bt_coex_lut_type ret;
u16 phy_ctx_id;
u32 primary_ch_phy_id, secondary_ch_phy_id;
/*
* Checking that we hold mvm->mutex is a good idea, but the rate
@ -535,7 +497,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
if (!chanctx_conf ||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
rcu_read_unlock();
return BT_COEX_LOOSE_LUT;
return BT_COEX_INVALID_LUT;
}
ret = BT_COEX_TX_DIS_LUT;
@ -546,10 +508,13 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
}
phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
secondary_ch_phy_id =
le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
if (primary_ch_phy_id == phy_ctx_id)
ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
else if (secondary_ch_phy_id == phy_ctx_id)
ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
/* else - default = TX TX disallowed */
@ -567,59 +532,63 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
int ret;
u32 flags;
u32 mode;
ret = iwl_send_bt_prio_tbl(mvm);
if (ret)
return ret;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_send_bt_init_conf_old(mvm);
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
if (!bt_cmd)
return -ENOMEM;
cmd.data[0] = bt_cmd;
bt_cmd->max_kill = 5;
bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
bt_cmd->bt4_tx_rx_max_freq0 = 15;
bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
lockdep_assert_held(&mvm->mutex);
flags = iwlwifi_mod_params.bt_coex_active ?
BT_COEX_NW : BT_COEX_DISABLE;
bt_cmd->flags = cpu_to_le32(flags);
if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
u32 mode;
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
BT_VALID_BT_PRIO_BOOST |
BT_VALID_MAX_KILL |
BT_VALID_3W_TMRS |
BT_VALID_KILL_ACK |
BT_VALID_KILL_CTS |
BT_VALID_REDUCED_TX_POWER |
BT_VALID_LUT |
BT_VALID_WIFI_RX_SW_PRIO_BOOST |
BT_VALID_WIFI_TX_SW_PRIO_BOOST |
BT_VALID_ANT_ISOLATION |
BT_VALID_ANT_ISOLATION_THRS |
BT_VALID_TXTX_DELTA_FREQ_THRS |
BT_VALID_TXRX_MAX_FREQ_0 |
BT_VALID_SYNC_TO_SCO);
switch (mvm->bt_force_ant_mode) {
case BT_FORCE_ANT_BT:
mode = BT_COEX_BT;
break;
case BT_FORCE_ANT_WIFI:
mode = BT_COEX_WIFI;
break;
default:
WARN_ON(1);
mode = 0;
}
bt_cmd->mode = cpu_to_le32(mode);
goto send_cmd;
}
bt_cmd->max_kill = cpu_to_le32(5);
bt_cmd->bt4_antenna_isolation_thr =
cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD);
bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
bt_cmd->mode = cpu_to_le32(mode);
if (IWL_MVM_BT_COEX_SYNC2SCO)
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
bt_cmd->enabled_modules |=
cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
if (IWL_MVM_BT_COEX_CORUNNING) {
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
BT_VALID_CORUN_LUT_40);
bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
}
if (IWL_MVM_BT_COEX_CORUNNING)
bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
if (IWL_MVM_BT_COEX_MPLUT) {
bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
bt_cmd->enabled_modules |=
cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
}
bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
if (mvm->cfg->bt_shared_single_ant)
memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
sizeof(iwl_single_shared_ant));
@ -627,21 +596,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
sizeof(iwl_combined_lookup));
/* Take first Co-running block LUT to get started */
memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
sizeof(bt_cmd->bt4_corun_lut20));
memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
sizeof(bt_cmd->bt4_corun_lut40));
memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
sizeof(iwl_bt_prio_boost));
memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut,
sizeof(iwl_bt_mprio_lut));
bt_cmd->kill_ack_msk =
cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
bt_cmd->kill_cts_msk =
cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
send_cmd:
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@ -651,19 +611,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
return ret;
}
static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
bool reduced_tx_power)
static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm,
bool reduced_tx_power)
{
enum iwl_bt_kill_msk bt_kill_msk;
struct iwl_bt_coex_cmd *bt_cmd;
struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
struct iwl_host_cmd cmd = {
.id = BT_CONFIG,
.data[0] = &bt_cmd,
.len = { sizeof(*bt_cmd), },
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
int ret = 0;
lockdep_assert_held(&mvm->mutex);
@ -693,40 +646,30 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
mvm->bt_kill_msk = bt_kill_msk;
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
if (!bt_cmd)
return -ENOMEM;
cmd.data[0] = bt_cmd;
bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
cmd.boost_values[0].kill_ack_msk =
cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
cmd.boost_values[0].kill_cts_msk =
cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
BT_VALID_KILL_ACK |
BT_VALID_KILL_CTS);
cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
iwl_bt_ack_kill_msk[bt_kill_msk],
iwl_bt_cts_kill_msk[bt_kill_msk]);
ret = iwl_mvm_send_cmd(mvm, &cmd);
kfree(bt_cmd);
return ret;
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
sizeof(cmd), &cmd);
}
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
bool enable)
{
struct iwl_bt_coex_cmd *bt_cmd;
/* Send ASYNC since this can be sent from an atomic context */
struct iwl_host_cmd cmd = {
.id = BT_CONFIG,
.len = { sizeof(*bt_cmd), },
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
.flags = CMD_ASYNC,
};
struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
struct iwl_mvm_sta *mvmsta;
u32 value;
int ret;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
@ -737,27 +680,20 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
if (mvmsta->bt_reduced_txpower == enable)
return 0;
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
if (!bt_cmd)
return -ENOMEM;
cmd.data[0] = bt_cmd;
bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
bt_cmd->valid_bit_msk =
cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
bt_cmd->bt_reduced_tx_power = sta_id;
value = mvmsta->sta_id;
if (enable)
bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
value |= BT_REDUCED_TX_POWER_BIT;
IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
enable ? "en" : "dis", sta_id);
cmd.reduced_txp = cpu_to_le32(value);
mvmsta->bt_reduced_txpower = enable;
ret = iwl_mvm_send_cmd(mvm, &cmd);
ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
sizeof(cmd), &cmd);
kfree(bt_cmd);
return ret;
}
@ -780,9 +716,9 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
mvmvif->bf_data.last_bt_coex_event = rssi;
mvmvif->bf_data.bt_coex_max_thold =
enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
mvmvif->bf_data.bt_coex_min_thold =
enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
}
/* must be called under rcu_read_lock */
@ -851,10 +787,13 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
if (!vif->bss_conf.assoc)
smps_mode = IEEE80211_SMPS_AUTOMATIC;
if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
mvmvif->phy_ctxt->id))
smps_mode = IEEE80211_SMPS_AUTOMATIC;
IWL_DEBUG_COEX(data->mvm,
"mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
mvmvif->id, data->notif->bt_status, bt_activity_grading,
smps_mode);
"mac %d: bt_activity_grading %d smps_req %d\n",
mvmvif->id, bt_activity_grading, smps_mode);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
@ -906,7 +845,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
*/
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
!data->notif->bt_status) {
le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
data->reduced_tx_power = false;
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
@ -919,7 +858,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
/* if the RSSI isn't valid, fake it is very low */
if (!ave_rssi)
ave_rssi = -100;
if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
@ -930,7 +869,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
* the iteration, if one interface's rssi isn't good enough,
* bt_kill_msk will be set to default values.
*/
} else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
@ -955,6 +894,10 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
struct iwl_bt_coex_ci_cmd cmd = {};
u8 ci_bw_idx;
/* Ignore updates if we are in force mode */
if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
return;
rcu_read_lock();
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@ -969,9 +912,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
if (chan->def.width < NL80211_CHAN_WIDTH_40) {
ci_bw_idx = 0;
cmd.co_run_bw_primary = 0;
} else {
cmd.co_run_bw_primary = 1;
if (chan->def.center_freq1 >
chan->def.chan->center_freq)
ci_bw_idx = 2;
@ -981,7 +922,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
cmd.bt_primary_ci =
iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
cmd.primary_ch_phy_id =
cpu_to_le32(*((u16 *)data.primary->drv_priv));
}
if (data.secondary) {
@ -993,9 +935,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
if (chan->def.width < NL80211_CHAN_WIDTH_40) {
ci_bw_idx = 0;
cmd.co_run_bw_secondary = 0;
} else {
cmd.co_run_bw_secondary = 1;
if (chan->def.center_freq1 >
chan->def.chan->center_freq)
ci_bw_idx = 2;
@ -1005,7 +945,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
cmd.bt_secondary_ci =
iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
cmd.secondary_ch_phy_id =
cpu_to_le32(*((u16 *)data.secondary->drv_priv));
}
rcu_read_unlock();
@ -1025,7 +966,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
@ -1036,11 +977,10 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd);
IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
notif->bt_status ? "ON" : "OFF");
IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
le32_to_cpu(notif->primary_ch_lut));
@ -1048,8 +988,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
le32_to_cpu(notif->secondary_ch_lut));
IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
le32_to_cpu(notif->bt_activity_grading));
IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
notif->bt_agg_traffic_load);
/* remember this notification for future use: rssi fluctuations */
memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@ -1119,8 +1057,17 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
};
int ret;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
return;
}
lockdep_assert_held(&mvm->mutex);
/* Ignore updates if we are in force mode */
if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
return;
/*
* Rssi update while not associated - can happen since the statistics
* are handled asynchronously
@ -1129,7 +1076,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return;
/* No BT - reports should be disabled */
if (!mvm->last_bt_notif.bt_status)
if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
return;
IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
@ -1160,7 +1107,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
@ -1171,15 +1118,23 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
enum iwl_bt_coex_lut_type lut_type;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
return LINK_QUAL_AGG_TIME_LIMIT_DEF;
if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
BT_HIGH_TRAFFIC)
return LINK_QUAL_AGG_TIME_LIMIT_DEF;
lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
if (lut_type == BT_COEX_LOOSE_LUT)
if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
return LINK_QUAL_AGG_TIME_LIMIT_DEF;
/* tight coex, high bt traffic, reduce AGG time limit */
@ -1190,18 +1145,37 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
enum iwl_bt_coex_lut_type lut_type;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
return true;
if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
BT_HIGH_TRAFFIC)
return true;
/*
* In Tight, BT can't Rx while we Tx, so use both antennas since BT is
* already killed.
* In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
* Tx.
* In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
* since BT is already killed.
* In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
* we Tx.
* When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
*/
return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
return lut_type != BT_COEX_LOOSE_LUT;
}
bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
{
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF;
}
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
@ -1209,6 +1183,9 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
{
u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
if (band != IEEE80211_BAND_2GHZ)
return false;
@ -1249,6 +1226,11 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
{
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
iwl_mvm_bt_coex_vif_change_old(mvm);
return;
}
iwl_mvm_bt_coex_notif_handle(mvm);
}
@ -1258,22 +1240,22 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 ant_isolation = le32_to_cpup((void *)pkt->data);
struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
u8 __maybe_unused lower_bound, upper_bound;
int ret;
u8 lut;
struct iwl_bt_coex_cmd *bt_cmd;
struct iwl_host_cmd cmd = {
.id = BT_CONFIG,
.len = { sizeof(*bt_cmd), },
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
if (!IWL_MVM_BT_COEX_CORUNNING)
return 0;
lockdep_assert_held(&mvm->mutex);
/* Ignore updates if we are in force mode */
if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
return 0;
if (ant_isolation == mvm->last_ant_isol)
return 0;
@ -1298,25 +1280,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
mvm->last_corun_lut = lut;
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
if (!bt_cmd)
return 0;
cmd.data[0] = bt_cmd;
bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
BT_VALID_CORUN_LUT_20 |
BT_VALID_CORUN_LUT_40);
/* For the moment, use the same LUT for 20GHz and 40GHz */
memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
sizeof(bt_cmd->bt4_corun_lut20));
memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
sizeof(cmd.corun_lut20));
memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
sizeof(bt_cmd->bt4_corun_lut40));
memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
sizeof(cmd.corun_lut40));
ret = iwl_mvm_send_cmd(mvm, &cmd);
kfree(bt_cmd);
return ret;
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
sizeof(cmd), &cmd);
}

File diff suppressed because it is too large Load diff

View file

@ -79,6 +79,8 @@
#define IWL_MVM_PS_SNOOZE_WINDOW 50
#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62
#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65
#define IWL_MVM_BT_COEX_SYNC2SCO 1
#define IWL_MVM_BT_COEX_CORUNNING 1
#define IWL_MVM_BT_COEX_MPLUT 1

View file

@ -312,20 +312,10 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
BT_MBOX_MSG(notif, _num, _field), \
true ? "\n" : ", ");
static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
static
int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
int pos, int bufsz)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
char *buf;
int ret, pos = 0, bufsz = sizeof(char) * 1024;
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&mvm->mutex);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
@ -378,25 +368,118 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
BT_MBOX_PRINT(3, SSN_2, false);
BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
notif->bt_status);
pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
notif->bt_open_conn);
pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
notif->bt_traffic_load);
pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
notif->bt_agg_traffic_load);
pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
notif->bt_ci_compliance);
pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
le32_to_cpu(notif->primary_ch_lut));
pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
le32_to_cpu(notif->secondary_ch_lut));
pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
le32_to_cpu(notif->bt_activity_grading));
pos += scnprintf(buf+pos, bufsz-pos,
"antenna isolation = %d CORUN LUT index = %d\n",
mvm->last_ant_isol, mvm->last_corun_lut);
return pos;
}
static
int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
char *buf, int pos, int bufsz)
{
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
BT_MBOX_PRINT(0, LE_PROF1, false);
BT_MBOX_PRINT(0, LE_PROF2, false);
BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
BT_MBOX_PRINT(0, CHL_SEQ_N, false);
BT_MBOX_PRINT(0, INBAND_S, false);
BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
BT_MBOX_PRINT(0, LE_SCAN, false);
BT_MBOX_PRINT(0, LE_ADV, false);
BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
BT_MBOX_PRINT(0, OPEN_CON_1, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
BT_MBOX_PRINT(1, IP_SR, false);
BT_MBOX_PRINT(1, LE_MSTR, false);
BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
BT_MBOX_PRINT(1, MSG_TYPE, false);
BT_MBOX_PRINT(1, SSN, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
BT_MBOX_PRINT(2, SNIFF_ACT, false);
BT_MBOX_PRINT(2, PAG, false);
BT_MBOX_PRINT(2, INQUIRY, false);
BT_MBOX_PRINT(2, CONN, false);
BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
BT_MBOX_PRINT(2, DISC, false);
BT_MBOX_PRINT(2, SCO_TX_ACT, false);
BT_MBOX_PRINT(2, SCO_RX_ACT, false);
BT_MBOX_PRINT(2, ESCO_RE_TX, false);
BT_MBOX_PRINT(2, SCO_DURATION, true);
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
BT_MBOX_PRINT(3, SCO_STATE, false);
BT_MBOX_PRINT(3, SNIFF_STATE, false);
BT_MBOX_PRINT(3, A2DP_STATE, false);
BT_MBOX_PRINT(3, ACL_STATE, false);
BT_MBOX_PRINT(3, MSTR_STATE, false);
BT_MBOX_PRINT(3, OBX_STATE, false);
BT_MBOX_PRINT(3, OPEN_CON_2, false);
BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
BT_MBOX_PRINT(3, INBAND_P, false);
BT_MBOX_PRINT(3, MSG_TYPE_2, false);
BT_MBOX_PRINT(3, SSN_2, false);
BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
return pos;
}
static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
char *buf;
int ret, pos = 0, bufsz = sizeof(char) * 1024;
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&mvm->mutex);
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
struct iwl_bt_coex_profile_notif_old *notif =
&mvm->last_bt_notif_old;
pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
notif->bt_ci_compliance);
pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
le32_to_cpu(notif->primary_ch_lut));
pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
le32_to_cpu(notif->secondary_ch_lut));
pos += scnprintf(buf+pos,
bufsz-pos, "bt_activity_grading = %d\n",
le32_to_cpu(notif->bt_activity_grading));
pos += scnprintf(buf+pos, bufsz-pos,
"antenna isolation = %d CORUN LUT index = %d\n",
mvm->last_ant_isol, mvm->last_corun_lut);
} else {
struct iwl_bt_coex_profile_notif *notif =
&mvm->last_bt_notif;
pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
notif->bt_ci_compliance);
pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
le32_to_cpu(notif->primary_ch_lut));
pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
le32_to_cpu(notif->secondary_ch_lut));
pos += scnprintf(buf+pos,
bufsz-pos, "bt_activity_grading = %d\n",
le32_to_cpu(notif->bt_activity_grading));
pos += scnprintf(buf+pos, bufsz-pos,
"antenna isolation = %d CORUN LUT index = %d\n",
mvm->last_ant_isol, mvm->last_corun_lut);
}
mutex_unlock(&mvm->mutex);
@ -411,28 +494,48 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
char buf[256];
int bufsz = sizeof(buf);
int pos = 0;
mutex_lock(&mvm->mutex);
pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
pos += scnprintf(buf+pos, bufsz-pos,
"\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
le64_to_cpu(cmd->bt_primary_ci),
!!cmd->co_run_bw_primary);
pos += scnprintf(buf+pos, bufsz-pos,
"\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
le64_to_cpu(cmd->bt_secondary_ci),
!!cmd->co_run_bw_secondary);
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
pos += scnprintf(buf+pos, bufsz-pos,
"Channel inhibition CMD\n");
pos += scnprintf(buf+pos, bufsz-pos,
"\tPrimary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_primary_ci));
pos += scnprintf(buf+pos, bufsz-pos,
"\tSecondary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_secondary_ci));
pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
} else {
struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
pos += scnprintf(buf+pos, bufsz-pos,
"Channel inhibition CMD\n");
pos += scnprintf(buf+pos, bufsz-pos,
"\tPrimary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_primary_ci));
pos += scnprintf(buf+pos, bufsz-pos,
"\tSecondary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_secondary_ci));
pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
}
mutex_unlock(&mvm->mutex);
@ -455,6 +558,43 @@ iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
return count;
}
static ssize_t
iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
static const char * const modes_str[BT_FORCE_ANT_MAX] = {
[BT_FORCE_ANT_DIS] = "dis",
[BT_FORCE_ANT_AUTO] = "auto",
[BT_FORCE_ANT_BT] = "bt",
[BT_FORCE_ANT_WIFI] = "wifi",
};
int ret, bt_force_ant_mode;
for (bt_force_ant_mode = 0;
bt_force_ant_mode < ARRAY_SIZE(modes_str);
bt_force_ant_mode++) {
if (!strcmp(buf, modes_str[bt_force_ant_mode]))
break;
}
if (bt_force_ant_mode >= ARRAY_SIZE(modes_str))
return -EINVAL;
ret = 0;
mutex_lock(&mvm->mutex);
if (mvm->bt_force_ant_mode == bt_force_ant_mode)
goto out;
mvm->bt_force_ant_mode = bt_force_ant_mode;
IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
modes_str[mvm->bt_force_ant_mode]);
ret = iwl_send_bt_init_conf(mvm);
out:
mutex_unlock(&mvm->mutex);
return ret ?: count;
}
#define PRINT_STATS_LE32(_str, _val) \
pos += scnprintf(buf + pos, bufsz - pos, \
fmt_table, _str, \
@ -1101,6 +1241,7 @@ MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
@ -1142,6 +1283,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);

View file

@ -72,10 +72,13 @@
* enum iwl_bt_coex_flags - flags for BT_COEX command
* @BT_COEX_MODE_POS:
* @BT_COEX_MODE_MSK:
* @BT_COEX_DISABLE:
* @BT_COEX_2W:
* @BT_COEX_3W:
* @BT_COEX_NW:
* @BT_COEX_DISABLE_OLD:
* @BT_COEX_2W_OLD:
* @BT_COEX_3W_OLD:
* @BT_COEX_NW_OLD:
* @BT_COEX_AUTO_OLD:
* @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
* @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
* @BT_COEX_SYNC2SCO:
* @BT_COEX_CORUNNING:
* @BT_COEX_MPLUT:
@ -85,10 +88,13 @@
enum iwl_bt_coex_flags {
BT_COEX_MODE_POS = 3,
BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS,
BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS,
BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS,
BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS,
BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS,
BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS,
BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS,
BT_COEX_SYNC2SCO = BIT(7),
BT_COEX_CORUNNING = BIT(8),
BT_COEX_MPLUT = BIT(9),
@ -151,7 +157,7 @@ enum iwl_bt_coex_lut_type {
#define BT_REDUCED_TX_POWER_BIT BIT(7)
/**
* struct iwl_bt_coex_cmd - bt coex configuration command
* struct iwl_bt_coex_cmd_old - bt coex configuration command
* @flags:&enum iwl_bt_coex_flags
* @max_kill:
* @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
@ -176,7 +182,7 @@ enum iwl_bt_coex_lut_type {
*
* The structure is used for the BT_COEX command.
*/
struct iwl_bt_coex_cmd {
struct iwl_bt_coex_cmd_old {
__le32 flags;
u8 max_kill;
u8 bt_reduced_tx_power;
@ -202,26 +208,117 @@ struct iwl_bt_coex_cmd {
__le32 valid_bit_msk;
} __packed; /* BT_COEX_CMD_API_S_VER_5 */
enum iwl_bt_coex_mode {
BT_COEX_DISABLE = 0x0,
BT_COEX_NW = 0x1,
BT_COEX_BT = 0x2,
BT_COEX_WIFI = 0x3,
}; /* BT_COEX_MODES_E */
enum iwl_bt_coex_enabled_modules {
BT_COEX_MPLUT_ENABLED = BIT(0),
BT_COEX_MPLUT_BOOST_ENABLED = BIT(1),
BT_COEX_SYNC2SCO_ENABLED = BIT(2),
BT_COEX_CORUN_ENABLED = BIT(3),
BT_COEX_HIGH_BAND_RET = BIT(4),
}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
/**
* struct iwl_bt_coex_cmd - bt coex configuration command
* @mode: enum %iwl_bt_coex_mode
* @enabled_modules: enum %iwl_bt_coex_enabled_modules
* @max_kill: max count of Tx retries due to kill from PTA
* @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
* should be set by default
* @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
* should be set by default
* @bt4_antenna_isolation_thr: antenna threshold value
* @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
* @bt4_tx_rx_max_freq0: TxRx max frequency
* @multiprio_lut: multi priority LUT configuration
* @mplut_prio_boost: BT priority boost registers
* @decision_lut: PTA decision LUT, per Prio-Ch
*
* The structure is used for the BT_COEX command.
*/
struct iwl_bt_coex_cmd {
__le32 mode;
__le32 enabled_modules;
__le32 max_kill;
__le32 override_primary_lut;
__le32 override_secondary_lut;
__le32 bt4_antenna_isolation_thr;
__le32 bt4_tx_tx_delta_freq_thr;
__le32 bt4_tx_rx_max_freq0;
__le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
__le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
__le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
} __packed; /* BT_COEX_CMD_API_S_VER_6 */
/**
* struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
* @corun_lut20: co-running 20 MHz LUT configuration
* @corun_lut40: co-running 40 MHz LUT configuration
*
* The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
*/
struct iwl_bt_coex_corun_lut_update_cmd {
__le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
__le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
/**
* struct iwl_bt_coex_sw_boost - SW boost values
* @wifi_tx_prio_boost: SW boost of wifi tx priority
* @wifi_rx_prio_boost: SW boost of wifi rx priority
* @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
* @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
*/
struct iwl_bt_coex_sw_boost {
__le32 wifi_tx_prio_boost;
__le32 wifi_rx_prio_boost;
__le32 kill_ack_msk;
__le32 kill_cts_msk;
};
/**
* struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
* @boost_values: check struct %iwl_bt_coex_sw_boost - one for each channel
* primary / secondary / low priority
*/
struct iwl_bt_coex_sw_boost_update_cmd {
struct iwl_bt_coex_sw_boost boost_values[3];
} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
/**
* struct iwl_bt_coex_reduced_txp_update_cmd
* @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
* bits are the sta_id (value)
*/
struct iwl_bt_coex_reduced_txp_update_cmd {
__le32 reduced_txp;
} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
/**
* struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
* @bt_primary_ci:
* @bt_secondary_ci:
* @co_run_bw_primary:
* @co_run_bw_secondary:
* @primary_ch_phy_id:
* @bt_secondary_ci:
* @secondary_ch_phy_id:
*
* Used for BT_COEX_CI command
*/
struct iwl_bt_coex_ci_cmd {
__le64 bt_primary_ci;
__le64 bt_secondary_ci;
__le32 primary_ch_phy_id;
u8 co_run_bw_primary;
u8 co_run_bw_secondary;
u8 primary_ch_phy_id;
u8 secondary_ch_phy_id;
} __packed; /* BT_CI_MSG_API_S_VER_1 */
__le64 bt_secondary_ci;
__le32 secondary_ch_phy_id;
} __packed; /* BT_CI_MSG_API_S_VER_2 */
#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
@ -290,33 +387,40 @@ enum iwl_bt_activity_grading {
BT_HIGH_TRAFFIC = 3,
}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
enum iwl_bt_ci_compliance {
BT_CI_COMPLIANCE_NONE = 0,
BT_CI_COMPLIANCE_PRIMARY = 1,
BT_CI_COMPLIANCE_SECONDARY = 2,
BT_CI_COMPLIANCE_BOTH = 3,
}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \
(_ttc_rrc_status & BIT(_phy_id))
#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \
((_ttc_rrc_status >> 4) & BIT(_phy_id))
/**
* struct iwl_bt_coex_profile_notif - notification about BT coex
* @mbox_msg: message from BT to WiFi
* @msg_idx: the index of the message
* @bt_status: 0 - off, 1 - on
* @bt_open_conn: number of BT connections open
* @bt_traffic_load: load of BT traffic
* @bt_agg_traffic_load: aggregated load of BT traffic
* @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
* @primary_ch_lut: LUT used for primary channel
* @secondary_ch_lut: LUT used for secondary channel
* @bt_ci_compliance: enum %iwl_bt_ci_compliance
* @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
* @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
* @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
* @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
*/
struct iwl_bt_coex_profile_notif {
__le32 mbox_msg[4];
__le32 msg_idx;
u8 bt_status;
u8 bt_open_conn;
u8 bt_traffic_load;
u8 bt_agg_traffic_load;
u8 bt_ci_compliance;
u8 reserved[3];
__le32 bt_ci_compliance;
__le32 primary_ch_lut;
__le32 secondary_ch_lut;
__le32 bt_activity_grading;
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
u8 ttc_rrc_status;
u8 reserved[3];
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
enum iwl_bt_coex_prio_table_event {
BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
@ -355,4 +459,54 @@ struct iwl_bt_coex_prio_tbl_cmd {
u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
} __packed;
/**
* struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
* @bt_primary_ci:
* @bt_secondary_ci:
* @co_run_bw_primary:
* @co_run_bw_secondary:
* @primary_ch_phy_id:
* @secondary_ch_phy_id:
*
* Used for BT_COEX_CI command
*/
struct iwl_bt_coex_ci_cmd_old {
__le64 bt_primary_ci;
__le64 bt_secondary_ci;
u8 co_run_bw_primary;
u8 co_run_bw_secondary;
u8 primary_ch_phy_id;
u8 secondary_ch_phy_id;
} __packed; /* BT_CI_MSG_API_S_VER_1 */
/**
* struct iwl_bt_coex_profile_notif_old - notification about BT coex
* @mbox_msg: message from BT to WiFi
* @msg_idx: the index of the message
* @bt_status: 0 - off, 1 - on
* @bt_open_conn: number of BT connections open
* @bt_traffic_load: load of BT traffic
* @bt_agg_traffic_load: aggregated load of BT traffic
* @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
* @primary_ch_lut: LUT used for primary channel
* @secondary_ch_lut: LUT used for secondary channel
* @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
*/
struct iwl_bt_coex_profile_notif_old {
__le32 mbox_msg[4];
__le32 msg_idx;
u8 bt_status;
u8 bt_open_conn;
u8 bt_traffic_load;
u8 bt_agg_traffic_load;
u8 bt_ci_compliance;
u8 ttc_enabled;
__le16 reserved;
__le32 primary_ch_lut;
__le32 secondary_ch_lut;
__le32 bt_activity_grading;
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
#endif /* __fw_api_bt_coex_h__ */

View file

@ -336,7 +336,7 @@ struct iwl_beacon_filter_cmd {
#define IWL_BF_DEBUG_FLAG_D0I3 0
#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
#define IWL_BF_ESCAPE_TIMER_D0I3 1024
#define IWL_BF_ESCAPE_TIMER_D0I3 0
#define IWL_BF_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0

View file

@ -169,19 +169,13 @@ enum iwl_scan_type {
SCAN_TYPE_DISCOVERY_FORCED = 6,
}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
/**
* Maximal number of channels to scan
* it should be equal to:
* max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
*/
#define MAX_NUM_SCAN_CHANNELS 50
/**
* struct iwl_scan_cmd - scan request command
* ( SCAN_REQUEST_CMD = 0x80 )
* @len: command length in bytes
* @scan_flags: scan flags from SCAN_FLAGS_*
* @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS)
* @channel_count: num of channels in channel list
* (1 - ucode_capa.n_scan_channels)
* @quiet_time: in msecs, dwell this time for active scan on quiet channels
* @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
* this number of packets were received (typically 1)
@ -345,7 +339,7 @@ struct iwl_scan_results_notif {
* @last_channel: last channel that was scanned
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
* @results: all scan results, only "scanned_channels" of them are valid
* @results: array of scan results, only "scanned_channels" of them are valid
*/
struct iwl_scan_complete_notif {
u8 scanned_channels;
@ -354,11 +348,10 @@ struct iwl_scan_complete_notif {
u8 last_channel;
__le32 tsf_low;
__le32 tsf_high;
struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
struct iwl_scan_results_notif results[];
} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
/* scan offload */
#define IWL_MAX_SCAN_CHANNELS 40
#define IWL_SCAN_MAX_BLACKLIST_LEN 64
#define IWL_SCAN_SHORT_BLACKLIST_LEN 16
#define IWL_SCAN_MAX_PROFILES 11
@ -423,36 +416,24 @@ enum iwl_scan_offload_channel_flags {
IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL = BIT(25),
};
/**
* iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S
* @type: bitmap - see enum iwl_scan_offload_channel_flags.
* 0: passive (0) or active (1) scan.
* 1-20: directed scan to i'th ssid.
* 22: channel width configuation - 1 for narrow.
* 24: full scan.
* 25: partial scan.
* @channel_number: channel number 1-13 etc.
* @iter_count: repetition count for the channel.
* @iter_interval: interval between two innteration on one channel.
* @dwell_time: entry 0 - active scan, entry 1 - passive scan.
/* channel configuration for struct iwl_scan_offload_cfg. Each channels needs:
* __le32 type: bitmap; bits 1-20 are for directed scan to i'th ssid and
* see enum iwl_scan_offload_channel_flags.
* __le16 channel_number: channel number 1-13 etc.
* __le16 iter_count: repetition count for the channel.
* __le32 iter_interval: interval between two innteration on one channel.
* u8 active_dwell.
* u8 passive_dwell.
*/
struct iwl_scan_channel_cfg {
__le32 type[IWL_MAX_SCAN_CHANNELS];
__le16 channel_number[IWL_MAX_SCAN_CHANNELS];
__le16 iter_count[IWL_MAX_SCAN_CHANNELS];
__le32 iter_interval[IWL_MAX_SCAN_CHANNELS];
u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2];
} __packed;
#define IWL_SCAN_CHAN_SIZE 14
/**
* iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S
* @scan_cmd: scan command fixed part
* @channel_cfg: scan channel configuration
* @data: probe request frames (one per band)
* @data: scan channel configuration and probe request frames
*/
struct iwl_scan_offload_cfg {
struct iwl_scan_offload_cmd scan_cmd;
struct iwl_scan_channel_cfg channel_cfg;
u8 data[0];
} __packed;
@ -528,7 +509,7 @@ struct iwl_scan_offload_profile_cfg {
* @full_scan_mul: number of partial scans before each full scan
*/
struct iwl_scan_offload_schedule {
u16 delay;
__le16 delay;
u8 iterations;
u8 full_scan_mul;
} __packed;
@ -601,4 +582,211 @@ struct iwl_sched_scan_results {
u8 reserved;
};
/* Unified LMAC scan API */
#define IWL_MVM_BASIC_PASSIVE_DWELL 110
/**
* iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
* @tx_flags: combination of TX_CMD_FLG_*
* @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
* cleared. Combination of RATE_MCS_*
* @sta_id: index of destination station in FW station table
* @reserved: for alignment and future use
*/
struct iwl_scan_req_tx_cmd {
__le32 tx_flags;
__le32 rate_n_flags;
u8 sta_id;
u8 reserved[3];
} __packed;
enum iwl_scan_channel_flags_lmac {
IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27),
IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28),
};
/**
* iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
* @flags: bits 1-20: directed scan to i'th ssid
* other bits &enum iwl_scan_channel_flags_lmac
* @channel_number: channel number 1-13 etc
* @iter_count: scan iteration on this channel
* @iter_interval: interval in seconds between iterations on one channel
*/
struct iwl_scan_channel_cfg_lmac {
__le32 flags;
__le16 channel_num;
__le16 iter_count;
__le32 iter_interval;
} __packed;
/*
* iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
* @offset: offset in the data block
* @len: length of the segment
*/
struct iwl_scan_probe_segment {
__le16 offset;
__le16 len;
} __packed;
/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
* @mac_header: first (and common) part of the probe
* @band_data: band specific data
* @common_data: last (and common) part of the probe
* @buf: raw data block
*/
struct iwl_scan_probe_req {
struct iwl_scan_probe_segment mac_header;
struct iwl_scan_probe_segment band_data[2];
struct iwl_scan_probe_segment common_data;
u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
} __packed;
enum iwl_scan_channel_flags {
IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0),
IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1),
IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2),
};
/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
* @flags: enum iwl_scan_channel_flgs
* @non_ebs_ratio: how many regular scan iteration before EBS
*/
struct iwl_scan_channel_opt {
__le16 flags;
__le16 non_ebs_ratio;
} __packed;
/**
* iwl_mvm_lmac_scan_flags
* @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
* without filtering.
* @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
* @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
* @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
* @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
* @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
*/
enum iwl_mvm_lmac_scan_flags {
IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0),
IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1),
IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2),
IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3),
IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4),
IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5),
};
enum iwl_scan_priority {
IWL_SCAN_PRIORITY_LOW,
IWL_SCAN_PRIORITY_MEDIUM,
IWL_SCAN_PRIORITY_HIGH,
};
/**
* iwl_scan_req_unified_lmac - SCAN_REQUEST_CMD_API_S_VER_1
* @reserved1: for alignment and future use
* @channel_num: num of channels to scan
* @active-dwell: dwell time for active channels
* @passive-dwell: dwell time for passive channels
* @fragmented-dwell: dwell time for fragmented passive scan
* @reserved2: for alignment and future use
* @rx_chain_selct: PHY_RX_CHAIN_* flags
* @scan_flags: &enum iwl_mvm_lmac_scan_flags
* @max_out_time: max time (in TU) to be out of associated channel
* @suspend_time: pause scan this long (TUs) when returning to service channel
* @flags: RXON flags
* @filter_flags: RXON filter
* @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
* @direct_scan: list of SSIDs for directed active scan
* @scan_prio: enum iwl_scan_priority
* @iter_num: number of scan iterations
* @delay: delay in seconds before first iteration
* @schedule: two scheduling plans. The first one is finite, the second one can
* be infinite.
* @channel_opt: channel optimization options, for full and partial scan
* @data: channel configuration and probe request packet.
*/
struct iwl_scan_req_unified_lmac {
/* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
__le32 reserved1;
u8 n_channels;
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
__le16 reserved2;
__le16 rx_chain_select;
__le32 scan_flags;
__le32 max_out_time;
__le32 suspend_time;
/* RX_ON_FLAGS_API_S_VER_1 */
__le32 flags;
__le32 filter_flags;
struct iwl_scan_req_tx_cmd tx_cmd[2];
struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
__le32 scan_prio;
/* SCAN_REQ_PERIODIC_PARAMS_API_S */
__le32 iter_num;
__le32 delay;
struct iwl_scan_offload_schedule schedule[2];
struct iwl_scan_channel_opt channel_opt[2];
u8 data[];
} __packed;
/**
* struct iwl_lmac_scan_results_notif - scan results for one channel -
* SCAN_RESULT_NTF_API_S_VER_3
* @channel: which channel the results are from
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz
* @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
* @num_probe_not_sent: # of request that weren't sent due to not enough time
* @duration: duration spent in channel, in usecs
*/
struct iwl_lmac_scan_results_notif {
u8 channel;
u8 band;
u8 probe_status;
u8 num_probe_not_sent;
__le32 duration;
} __packed;
/**
* struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
* SCAN_COMPLETE_NTF_API_S_VER_3
* @scanned_channels: number of channels scanned (and number of valid results)
* @status: one of SCAN_COMP_STATUS_*
* @bt_status: BT on/off status
* @last_channel: last channel that was scanned
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
* @results: an array of scan results, only "scanned_channels" of them are valid
*/
struct iwl_lmac_scan_complete_notif {
u8 scanned_channels;
u8 status;
u8 bt_status;
u8 last_channel;
__le32 tsf_low;
__le32 tsf_high;
struct iwl_scan_results_notif results[];
} __packed;
/**
* iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
* @last_schedule_line: last schedule line executed (fast or regular)
* @last_schedule_iteration: last scan iteration executed before scan abort
* @status: enum iwl_scan_offload_complete_status
* @ebs_status: EBS success status &enum iwl_scan_ebs_status
* @time_after_last_iter; time in seconds elapsed after last iteration
*/
struct iwl_periodic_scan_complete {
u8 last_schedule_line;
u8 last_schedule_iteration;
u8 status;
u8 ebs_status;
__le32 time_after_last_iter;
__le32 reserved;
} __packed;
#endif

View file

@ -67,7 +67,7 @@
* enum iwl_sta_flags - flags for the ADD_STA host command
* @STA_FLG_REDUCED_TX_PWR_CTRL:
* @STA_FLG_REDUCED_TX_PWR_DATA:
* @STA_FLG_FLG_ANT_MSK: Antenna selection
* @STA_FLG_DISABLE_TX: set if TX should be disabled
* @STA_FLG_PS: set if STA is in Power Save
* @STA_FLG_INVALID: set if STA is invalid
* @STA_FLG_DLP_EN: Direct Link Protocol is enabled
@ -91,10 +91,7 @@ enum iwl_sta_flags {
STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3),
STA_FLG_REDUCED_TX_PWR_DATA = BIT(6),
STA_FLG_FLG_ANT_A = (1 << 4),
STA_FLG_FLG_ANT_B = (2 << 4),
STA_FLG_FLG_ANT_MSK = (STA_FLG_FLG_ANT_A |
STA_FLG_FLG_ANT_B),
STA_FLG_DISABLE_TX = BIT(4),
STA_FLG_PS = BIT(8),
STA_FLG_DRAIN_FLOW = BIT(12),

View file

@ -69,10 +69,8 @@
* @TX_CMD_FLG_ACK: expect ACK from receiving station
* @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
* Otherwise, use rate_n_flags from the TX command
* @TX_CMD_FLG_BA: this frame is a block ack
* @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
* Must set TX_CMD_FLG_ACK with this flag.
* @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
* @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
* @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
* @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
@ -82,12 +80,10 @@
* @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
* Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
* @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
* @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
* @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
* Should be set for beacons and probe responses
* @TX_CMD_FLG_CALIB: activate PA TX power calibrations
* @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
* @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
* @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
* Should be set for 26/30 length MAC headers
* @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
@ -103,7 +99,6 @@ enum iwl_tx_flags {
TX_CMD_FLG_PROT_REQUIRE = BIT(0),
TX_CMD_FLG_ACK = BIT(3),
TX_CMD_FLG_STA_RATE = BIT(4),
TX_CMD_FLG_BA = BIT(5),
TX_CMD_FLG_BAR = BIT(6),
TX_CMD_FLG_TXOP_PROT = BIT(7),
TX_CMD_FLG_VHT_NDPA = BIT(8),
@ -113,11 +108,9 @@ enum iwl_tx_flags {
TX_CMD_FLG_BT_DIS = BIT(12),
TX_CMD_FLG_SEQ_CTL = BIT(13),
TX_CMD_FLG_MORE_FRAG = BIT(14),
TX_CMD_FLG_NEXT_FRAME = BIT(15),
TX_CMD_FLG_TSF = BIT(16),
TX_CMD_FLG_CALIB = BIT(17),
TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18),
TX_CMD_FLG_AGG_START = BIT(19),
TX_CMD_FLG_MH_PAD = BIT(20),
TX_CMD_FLG_RESP_TO_DRV = BIT(21),
TX_CMD_FLG_CCMP_AGG = BIT(22),
@ -191,8 +184,6 @@ enum iwl_tx_flags {
* struct iwl_tx_cmd - TX command struct to FW
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @next_frame_len: same as len, but for next frame (0 if not applicable)
* Used for fragmentation and bursting, but not in 11n aggregation.
* @tx_flags: combination of TX_CMD_FLG_*
* @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
* cleared. Combination of RATE_MCS_*
@ -210,8 +201,6 @@ enum iwl_tx_flags {
* @data_retry_limit: max attempts to send the data packet
* @tid_spec: TID/tspec
* @pm_frame_timeout: PM TX frame timeout
* @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
* specified by HCCA protocol
*
* The byte count (both len and next_frame_len) includes MAC header
* (24/26/30/32 bytes)
@ -241,8 +230,7 @@ struct iwl_tx_cmd {
u8 initial_rate_index;
u8 reserved2;
u8 key[16];
__le16 next_frame_flags;
__le16 reserved3;
__le32 reserved3;
__le32 life_time;
__le32 dram_lsb_ptr;
u8 dram_msb_ptr;
@ -250,7 +238,7 @@ struct iwl_tx_cmd {
u8 data_retry_limit;
u8 tid_tspec;
__le16 pm_frame_timeout;
__le16 driver_txop;
__le16 reserved4;
u8 payload[0];
struct ieee80211_hdr hdr[0];
} __packed; /* TX_CMD_API_S_VER_3 */
@ -548,6 +536,20 @@ struct iwl_beacon_notif {
__le32 ibss_mgr_status;
} __packed;
/**
* struct iwl_extended_beacon_notif - notifies about beacon transmission
* @beacon_notify_hdr: tx response command associated with the beacon
* @tsf: last beacon tsf
* @ibss_mgr_status: whether IBSS is manager
* @gp2: last beacon time in gp2
*/
struct iwl_extended_beacon_notif {
struct iwl_mvm_tx_resp beacon_notify_hdr;
__le64 tsf;
__le32 ibss_mgr_status;
__le32 gp2;
} __packed; /* BEACON_NTFY_API_S_VER_5 */
/**
* enum iwl_dump_control - dump (flush) control flags
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty

View file

@ -86,6 +86,8 @@ enum {
#define IWL_MVM_STATION_COUNT 16
#define IWL_MVM_TDLS_STA_COUNT 4
/* commands */
enum {
MVM_ALIVE = 0x1,
@ -135,6 +137,7 @@ enum {
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
MATCH_FOUND_NOTIFICATION = 0xd9,
SCAN_ITERATION_COMPLETE = 0xe7,
/* Phy */
PHY_CONFIGURATION_CMD = 0x6a,
@ -163,7 +166,6 @@ enum {
BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98,
BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f,
@ -185,6 +187,10 @@ enum {
BT_COEX_PRIO_TABLE = 0xcc,
BT_COEX_PROT_ENV = 0xcd,
BT_PROFILE_NOTIFICATION = 0xce,
BT_CONFIG = 0x9b,
BT_COEX_UPDATE_SW_BOOST = 0x5a,
BT_COEX_UPDATE_CORUN_LUT = 0x5b,
BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
BT_COEX_CI = 0x5d,
REPLY_SF_CFG_CMD = 0xd1,
@ -534,6 +540,9 @@ enum iwl_time_event_type {
/* WiDi Sync Events */
TE_WIDI_TX_SYNC,
/* Channel Switch NoA */
TE_P2P_GO_CSA_NOA,
TE_MAX
}; /* MAC_EVENT_TYPE_API_E_VER_1 */

View file

@ -67,6 +67,7 @@
#include "iwl-prph.h"
#include "fw-api.h"
#include "mvm.h"
#include "time-event.h"
const u8 iwl_mvm_ac_to_tx_fifo[] = {
IWL_MVM_TX_FIFO_VO,
@ -667,10 +668,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
if (vif->bss_conf.qos)
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
if (vif->bss_conf.use_cts_prot) {
if (vif->bss_conf.use_cts_prot)
cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
}
IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
vif->bss_conf.use_cts_prot,
vif->bss_conf.ht_operation_mode);
@ -904,7 +904,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
struct iwl_mac_beacon_cmd beacon_cmd = {};
struct ieee80211_tx_info *info;
u32 beacon_skb_len;
u32 rate;
u32 rate, tx_flags;
if (WARN_ON(!beacon))
return -EINVAL;
@ -914,14 +914,17 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
/* TODO: for now the beacon template id is set to be the mac context id.
* Might be better to handle it as another resource ... */
beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
info = IEEE80211_SKB_CB(beacon);
/* Set up TX command fields */
beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
TX_CMD_FLG_BT_DIS |
TX_CMD_FLG_TSF);
tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
tx_flags |=
iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
TX_CMD_FLG_BT_PRIO_POS;
beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
mvm->mgmt_last_antenna_idx =
iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
@ -931,8 +934,6 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
RATE_MCS_ANT_POS);
info = IEEE80211_SKB_CB(beacon);
if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
rate = IWL_FIRST_OFDM_RATE;
} else {
@ -969,7 +970,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
WARN_ON(vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_ADHOC);
beacon = ieee80211_beacon_get(mvm->hw, vif);
beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
if (!beacon)
return -ENOMEM;
@ -1200,31 +1201,94 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
return 0;
}
static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
struct ieee80211_vif *csa_vif, u32 gp2)
{
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(csa_vif);
if (!ieee80211_csa_is_complete(csa_vif)) {
int c = ieee80211_csa_update_counter(csa_vif);
iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
if (csa_vif->p2p &&
!iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
u32 rel_time = (c + 1) *
csa_vif->bss_conf.beacon_int -
IWL_MVM_CHANNEL_SWITCH_TIME;
u32 apply_time = gp2 + rel_time * 1024;
iwl_mvm_schedule_csa_noa(mvm, csa_vif,
IWL_MVM_CHANNEL_SWITCH_TIME -
IWL_MVM_CHANNEL_SWITCH_MARGIN,
apply_time);
}
} else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
/* we don't have CSA NoA scheduled yet, switch now */
ieee80211_csa_finish(csa_vif);
RCU_INIT_POINTER(mvm->csa_vif, NULL);
}
}
int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_beacon_notif *beacon = (void *)pkt->data;
u16 status __maybe_unused =
le16_to_cpu(beacon->beacon_notify_hdr.status.status);
u32 rate __maybe_unused =
le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
struct iwl_mvm_tx_resp *beacon_notify_hdr;
struct ieee80211_vif *csa_vif;
struct ieee80211_vif *tx_blocked_vif;
u64 tsf;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
status & TX_STATUS_MSK,
beacon->beacon_notify_hdr.failure_frame,
le64_to_cpu(beacon->tsf),
rate);
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
} else {
ieee80211_csa_finish(mvm->csa_vif);
mvm->csa_vif = NULL;
beacon_notify_hdr = &beacon->beacon_notify_hdr;
tsf = le64_to_cpu(beacon->tsf);
mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
} else {
struct iwl_beacon_notif *beacon = (void *)pkt->data;
beacon_notify_hdr = &beacon->beacon_notify_hdr;
tsf = le64_to_cpu(beacon->tsf);
}
IWL_DEBUG_RX(mvm,
"beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
le16_to_cpu(beacon_notify_hdr->status.status) &
TX_STATUS_MSK,
beacon_notify_hdr->failure_frame, tsf,
mvm->ap_last_beacon_gp2,
le32_to_cpu(beacon_notify_hdr->initial_rate));
csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(csa_vif && csa_vif->csa_active))
iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(tx_blocked_vif)) {
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(tx_blocked_vif);
/*
* The channel switch is started and we have blocked the
* stations. If this is the first beacon (the timeout wasn't
* set), set the unblock timeout, otherwise countdown
*/
if (!mvm->csa_tx_block_bcn_timeout)
mvm->csa_tx_block_bcn_timeout =
IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
else
mvm->csa_tx_block_bcn_timeout--;
/* Check if the timeout is expired, and unblock tx */
if (mvm->csa_tx_block_bcn_timeout == 0) {
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
}
}

View file

@ -80,6 +80,8 @@
#include "fw-api-scan.h"
#include "iwl-phy-db.h"
#include "testmode.h"
#include "iwl-fw-error-dump.h"
#include "iwl-prph.h"
static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
{
@ -241,6 +243,21 @@ iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
}
}
static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
iwl_mvm_ref(mvm, ref_type);
if (!wait_event_timeout(mvm->d0i3_exit_waitq,
!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
HZ)) {
WARN_ON_ONCE(1);
iwl_mvm_unref(mvm, ref_type);
return -EIO;
}
return 0;
}
static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
{
int i;
@ -276,6 +293,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TIMING_BEACON_ONLY |
IEEE80211_HW_CONNECTION_MONITOR |
IEEE80211_HW_CHANCTX_STA_CSA |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
@ -303,6 +321,16 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
}
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
!iwlwifi_mod_params.uapsd_disable) {
hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
hw->uapsd_queues = IWL_UAPSD_AC_INFO;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
}
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif);
hw->chanctx_data_size = sizeof(u16);
@ -374,6 +402,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
NL80211_FEATURE_LOW_PRIORITY_SCAN |
NL80211_FEATURE_P2P_GO_OPPPS;
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
@ -549,9 +578,6 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_OPERATIONAL:
iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
tx_agg_ref = true;
/*
* for tx start, wait synchronously until D0i3 exit to
* get the correct sequence number for the tid.
@ -560,12 +586,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
* by the trans layer (unlike commands), so wait for
* d0i3 exit in these cases as well.
*/
if (!wait_event_timeout(mvm->d0i3_exit_waitq,
!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
WARN_ON_ONCE(1);
iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
return -EIO;
}
ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
if (ret)
return ret;
tx_agg_ref = true;
break;
default:
break;
@ -637,6 +662,104 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
mvmvif->phy_ctxt = NULL;
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
{
struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_error_dump_data *dump_data;
struct iwl_fw_error_dump_info *dump_info;
const struct fw_img *img;
u32 sram_len, sram_ofs;
u32 file_len, rxf_len;
unsigned long flags;
u32 trans_len;
int reg_val;
lockdep_assert_held(&mvm->mutex);
if (mvm->fw_error_dump)
return;
img = &mvm->fw->img[mvm->cur_ucode];
sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
/* reading buffer size */
reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
/* the register holds the value divided by 128 */
rxf_len = rxf_len << 7;
file_len = sizeof(*dump_file) +
sizeof(*dump_data) * 3 +
sram_len +
rxf_len +
sizeof(*dump_info);
trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
if (trans_len)
file_len += trans_len;
dump_file = vzalloc(file_len);
if (!dump_file)
return;
mvm->fw_error_dump = dump_file;
dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
dump_file->file_len = cpu_to_le32(file_len);
dump_data = (void *)dump_file->data;
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
dump_data->len = cpu_to_le32(sizeof(*dump_info));
dump_info = (void *) dump_data->data;
dump_info->device_family =
mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
sizeof(dump_info->fw_human_readable));
strncpy(dump_info->dev_human_readable, mvm->cfg->name,
sizeof(dump_info->dev_human_readable));
strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
sizeof(dump_info->bus_human_readable));
dump_data = iwl_fw_error_next_data(dump_data);
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
dump_data->len = cpu_to_le32(rxf_len);
if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
u32 *rxf = (void *)dump_data->data;
int i;
for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
iwl_trans_write_prph(mvm->trans,
RXF_LD_FENCE_OFFSET_ADDR,
i * sizeof(u32));
rxf[i] = iwl_trans_read_prph(mvm->trans,
RXF_FIFO_RD_FENCE_ADDR);
}
iwl_trans_release_nic_access(mvm->trans, &flags);
}
dump_data = iwl_fw_error_next_data(dump_data);
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
dump_data->len = cpu_to_le32(sram_len);
iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
sram_len);
if (trans_len) {
void *buf = iwl_fw_error_next_data(dump_data);
u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
trans_len);
dump_data = (void *)((u8 *)buf + real_trans_len);
dump_file->file_len =
cpu_to_le32(file_len - trans_len + real_trans_len);
}
}
#endif
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{
#ifdef CONFIG_IWLWIFI_DEBUGFS
@ -688,6 +811,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
iwl_mvm_restart_cleanup(mvm);
ret = iwl_mvm_up(mvm);
if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
/* Something went wrong - we need to finish some cleanup
* that normally iwl_mvm_mac_restart_complete() below
* would do.
*/
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
iwl_mvm_d0i3_enable_tx(mvm, NULL);
}
mutex_unlock(&mvm->mutex);
return ret;
@ -785,6 +918,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
/*
* make sure D0i3 exit is completed, otherwise a target access
* during tx queue configuration could be done when still in
* D0i3 state.
*/
ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
if (ret)
return ret;
/*
* Not much to do here. The stack will not allow interface
* types or combinations that we didn't advertise, so we
@ -899,6 +1041,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
out_unlock:
mutex_unlock(&mvm->mutex);
iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
return ret;
}
@ -1159,8 +1303,12 @@ static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
bcast_mac = &cmd->macs[mvmvif->id];
/* enable filtering only for associated stations */
if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
/*
* enable filtering only for associated stations, but not for P2P
* Clients
*/
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
!vif->bss_conf.assoc)
return;
bcast_mac->default_discard = 1;
@ -1237,10 +1385,6 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
return 0;
/* bcast filtering isn't supported for P2P client */
if (vif->p2p)
return 0;
if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
return 0;
@ -1278,7 +1422,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (changes & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
/* add quota for this interface */
ret = iwl_mvm_update_quotas(mvm, vif);
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret) {
IWL_ERR(mvm, "failed to update quotas\n");
return;
@ -1425,7 +1569,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
/* power updated needs to be done before quotas */
iwl_mvm_power_update_mac(mvm);
ret = iwl_mvm_update_quotas(mvm, vif);
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
goto out_quota_failed;
@ -1463,7 +1607,20 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
/* Handle AP stop while in CSA */
if (rcu_access_pointer(mvm->csa_vif) == vif) {
iwl_mvm_remove_time_event(mvm, mvmvif,
&mvmvif->time_event_data);
RCU_INIT_POINTER(mvm->csa_vif, NULL);
}
if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
mvm->csa_tx_block_bcn_timeout = 0;
}
mvmvif->ap_ibss_active = false;
mvm->ap_last_beacon_gp2 = 0;
iwl_mvm_bt_coex_vif_change(mvm);
@ -1517,7 +1674,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
iwl_mvm_sched_scan_stop(mvm, true);
iwl_mvm_scan_offload_stop(mvm, true);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
@ -1537,19 +1694,21 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
struct ieee80211_scan_request *hw_req)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct cfg80211_scan_request *req = &hw_req->req;
int ret;
if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
if (req->n_channels == 0 ||
req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
return -EINVAL;
mutex_lock(&mvm->mutex);
switch (mvm->scan_status) {
case IWL_MVM_SCAN_SCHED:
ret = iwl_mvm_sched_scan_stop(mvm, true);
ret = iwl_mvm_scan_offload_stop(mvm, true);
if (ret) {
ret = -EBUSY;
goto out;
@ -1564,7 +1723,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
ret = iwl_mvm_scan_request(mvm, vif, req);
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
else
ret = iwl_mvm_scan_request(mvm, vif, req);
if (ret)
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
out:
@ -1680,6 +1843,70 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
int count = 0;
int i;
lockdep_assert_held(&mvm->mutex);
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex));
if (!sta || IS_ERR(sta) || !sta->tdls)
continue;
if (vif) {
mvmsta = iwl_mvm_sta_from_mac80211(sta);
if (mvmsta->vif != vif)
continue;
}
count++;
}
return count;
}
static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool sta_added)
{
int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
/*
* Disable ps when the first TDLS sta is added and re-enable it
* when the last TDLS sta is removed
*/
if ((tdls_sta_cnt == 1 && sta_added) ||
(tdls_sta_cnt == 0 && !sta_added))
iwl_mvm_power_update_mac(mvm);
}
static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
{
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
int i;
lockdep_assert_held(&mvm->mutex);
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex));
if (!sta || IS_ERR(sta) || !sta->tdls)
continue;
mvmsta = iwl_mvm_sta_from_mac80211(sta);
ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
NL80211_TDLS_TEARDOWN,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
GFP_KERNEL);
}
}
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@ -1718,7 +1945,20 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = -EINVAL;
goto out_unlock;
}
if (sta->tdls &&
(vif->p2p ||
iwl_mvm_tdls_sta_count(mvm, NULL) ==
IWL_MVM_TDLS_STA_COUNT ||
iwl_mvm_phy_ctx_count(mvm) > 1)) {
IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
ret = -EBUSY;
goto out_unlock;
}
ret = iwl_mvm_add_sta(mvm, vif, sta);
if (sta->tdls && ret == 0)
iwl_mvm_recalc_tdls_state(mvm, vif, true);
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_AUTH) {
/*
@ -1736,6 +1976,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
true);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
/* we don't support TDLS during DCM */
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
/* enable beacon filtering */
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
ret = 0;
@ -1753,6 +1998,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
ret = iwl_mvm_rm_sta(mvm, vif, sta);
if (sta->tdls)
iwl_mvm_recalc_tdls_state(mvm, vif, false);
} else {
ret = -EIO;
}
@ -1824,10 +2071,22 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
mutex_lock(&mvm->mutex);
/* Protect the session to hear the TDLS setup response on the channel */
iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
mutex_unlock(&mvm->mutex);
}
static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
struct ieee80211_scan_ies *ies)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@ -1865,15 +2124,21 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
mvm->scan_status = IWL_MVM_SCAN_SCHED;
ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
if (ret)
goto err;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
if (ret)
goto err;
}
ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
if (ret)
goto err;
ret = iwl_mvm_sched_scan_start(mvm, req);
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
else
ret = iwl_mvm_sched_scan_start(mvm, req);
if (!ret)
goto out;
err:
@ -1892,7 +2157,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
int ret;
mutex_lock(&mvm->mutex);
ret = iwl_mvm_sched_scan_stop(mvm, false);
ret = iwl_mvm_scan_offload_stop(mvm, false);
mutex_unlock(&mvm->mutex);
iwl_mvm_wait_for_async_handlers(mvm);
@ -2126,17 +2391,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
return 0;
}
static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt;
int ret;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
mutex_lock(&mvm->mutex);
phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
if (!phy_ctxt) {
ret = -ENOSPC;
@ -2154,19 +2419,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
*phy_ctxt_id = phy_ctxt->id;
out:
mutex_unlock(&mvm->mutex);
return ret;
}
static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
ret = __iwl_mvm_add_chanctx(mvm, ctx);
mutex_unlock(&mvm->mutex);
return ret;
}
static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
struct ieee80211_chanctx_conf *ctx)
{
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
lockdep_assert_held(&mvm->mutex);
iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
}
static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
mutex_lock(&mvm->mutex);
iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
__iwl_mvm_remove_chanctx(mvm, ctx);
mutex_unlock(&mvm->mutex);
}
@ -2195,17 +2481,17 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx,
bool switching_chanctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
mutex_lock(&mvm->mutex);
lockdep_assert_held(&mvm->mutex);
mvmvif->phy_ctxt = phy_ctxt;
@ -2222,18 +2508,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
* (in bss_info_changed), similarly for IBSS.
*/
ret = 0;
goto out_unlock;
goto out;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_MONITOR:
break;
default:
ret = -EINVAL;
goto out_unlock;
goto out;
}
ret = iwl_mvm_binding_add_vif(mvm, vif);
if (ret)
goto out_unlock;
goto out;
/*
* Power state must be updated before quotas,
@ -2247,65 +2533,162 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
*/
if (vif->type == NL80211_IFTYPE_MONITOR) {
mvmvif->monitor_active = true;
ret = iwl_mvm_update_quotas(mvm, vif);
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
goto out_remove_binding;
}
/* Handle binding during CSA */
if (vif->type == NL80211_IFTYPE_AP) {
iwl_mvm_update_quotas(mvm, vif);
if ((vif->type == NL80211_IFTYPE_AP) ||
(switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
iwl_mvm_update_quotas(mvm, NULL);
iwl_mvm_mac_ctxt_changed(mvm, vif, false);
}
goto out_unlock;
goto out;
out_remove_binding:
out_remove_binding:
iwl_mvm_binding_remove_vif(mvm, vif);
iwl_mvm_power_update_mac(mvm);
out_unlock:
mutex_unlock(&mvm->mutex);
out:
if (ret)
mvmvif->phy_ctxt = NULL;
return ret;
}
static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
mutex_unlock(&mvm->mutex);
return ret;
}
static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx,
bool switching_chanctx)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_vif *disabled_vif = NULL;
lockdep_assert_held(&mvm->mutex);
iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
switch (vif->type) {
case NL80211_IFTYPE_ADHOC:
goto out;
case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false;
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
if (!vif->csa_active || !mvmvif->ap_ibss_active)
goto out;
/* Set CS bit on all the stations */
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
/* Save blocked iface, the timeout is set on the next beacon */
rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
mvmvif->ap_ibss_active = false;
break;
case NL80211_IFTYPE_STATION:
if (!switching_chanctx)
break;
disabled_vif = vif;
iwl_mvm_mac_ctxt_changed(mvm, vif, true);
break;
default:
break;
}
iwl_mvm_update_quotas(mvm, disabled_vif);
iwl_mvm_binding_remove_vif(mvm, vif);
out:
mvmvif->phy_ctxt = NULL;
iwl_mvm_power_update_mac(mvm);
}
static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
mutex_lock(&mvm->mutex);
__iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
mutex_unlock(&mvm->mutex);
}
iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs,
enum ieee80211_chanctx_switch_mode mode)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
switch (vif->type) {
case NL80211_IFTYPE_ADHOC:
goto out_unlock;
case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false;
iwl_mvm_update_quotas(mvm, NULL);
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
if (!vif->csa_active || !mvmvif->ap_ibss_active)
goto out_unlock;
/* we only support SWAP_CONTEXTS and with a single-vif right now */
if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
return -EOPNOTSUPP;
mvmvif->ap_ibss_active = false;
iwl_mvm_update_quotas(mvm, NULL);
/*TODO: bt_coex notification here? */
default:
break;
mutex_lock(&mvm->mutex);
__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
if (ret) {
IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
goto out_reassign;
}
iwl_mvm_binding_remove_vif(mvm, vif);
ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
true);
if (ret) {
IWL_ERR(mvm,
"failed to assign new_ctx during channel switch\n");
goto out_remove;
}
out_unlock:
mvmvif->phy_ctxt = NULL;
iwl_mvm_power_update_mac(mvm);
goto out;
out_remove:
__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
out_reassign:
ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
if (ret) {
IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
goto out_restart;
}
ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
true);
if (ret) {
IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
goto out_restart;
}
goto out;
out_restart:
/* things keep failing, better restart the hw */
iwl_mvm_nic_restart(mvm, false);
out:
mutex_unlock(&mvm->mutex);
return ret;
}
static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
@ -2395,15 +2778,19 @@ static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
mutex_lock(&mvm->mutex);
if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (WARN(csa_vif && csa_vif->csa_active,
"Another CSA is already in progress"))
goto out_unlock;
IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
chandef->center_freq1);
mvm->csa_vif = vif;
rcu_assign_pointer(mvm->csa_vif, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
@ -2460,6 +2847,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.sta_rc_update = iwl_mvm_sta_rc_update,
.conf_tx = iwl_mvm_mac_conf_tx,
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
.mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
.flush = iwl_mvm_mac_flush,
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
@ -2472,6 +2860,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.change_chanctx = iwl_mvm_change_chanctx,
.assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
.unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
.switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
.start_ap = iwl_mvm_start_ap_ibss,
.stop_ap = iwl_mvm_stop_ap_ibss,

View file

@ -83,6 +83,24 @@
#define IWL_RSSI_OFFSET 50
#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
/*
* The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
* TBTT. This value should be big enough to ensure that we switch in time.
*/
#define IWL_MVM_CHANNEL_SWITCH_TIME 40
/*
* This value (in TUs) is used to fine tune the CSA NoA end time which should
* be just before "beacon 0" TBTT.
*/
#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
/*
* Number of beacons to transmit on a new channel until we unblock tx to
* the stations, even if we didn't identify them on a new channel
*/
#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_BK = 0,
IWL_MVM_TX_FIFO_BE,
@ -230,11 +248,21 @@ enum iwl_mvm_ref_type {
IWL_MVM_REF_USER,
IWL_MVM_REF_TX,
IWL_MVM_REF_TX_AGG,
IWL_MVM_REF_ADD_IF,
IWL_MVM_REF_EXIT_WORK,
IWL_MVM_REF_COUNT,
};
enum iwl_bt_force_ant_mode {
BT_FORCE_ANT_DIS = 0,
BT_FORCE_ANT_AUTO,
BT_FORCE_ANT_BT,
BT_FORCE_ANT_WIFI,
BT_FORCE_ANT_MAX,
};
/**
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
@ -523,7 +551,7 @@ struct iwl_mvm {
/* Scan status, cmd (pre-allocated) and auxiliary station */
enum iwl_scan_status scan_status;
struct iwl_scan_cmd *scan_cmd;
void *scan_cmd;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
/* rx chain antennas set through debugfs for the scan command */
@ -586,10 +614,6 @@ struct iwl_mvm {
/* -1 for always, 0 for never, >0 for that many times */
s8 restart_fw;
void *fw_error_dump;
void *fw_error_sram;
u32 fw_error_sram_len;
u32 *fw_error_rxf;
u32 fw_error_rxf_len;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;
@ -624,11 +648,16 @@ struct iwl_mvm {
/* BT-Coex */
u8 bt_kill_msk;
struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
struct iwl_bt_coex_profile_notif last_bt_notif;
struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
u32 last_ant_isol;
u8 last_corun_lut;
u8 bt_tx_prio;
enum iwl_bt_force_ant_mode bt_force_ant_mode;
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
@ -647,7 +676,12 @@ struct iwl_mvm {
/* Indicate if device power save is allowed */
bool ps_disabled;
struct ieee80211_vif *csa_vif;
struct ieee80211_vif __rcu *csa_vif;
struct ieee80211_vif __rcu *csa_tx_blocked_vif;
u8 csa_tx_block_bcn_timeout;
/* system time of last beacon (for AP/GO interface) */
u32 ap_last_beacon_gp2;
};
/* Extract MVM priv from op_mode and _hw */
@ -719,11 +753,6 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
struct ieee80211_tx_rate *r);
u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
#endif
u8 first_antenna(u8 mask);
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
@ -809,6 +838,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
/* MAC (virtual interface) programming */
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@ -835,7 +865,8 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
/* Quota management */
int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
struct ieee80211_vif *disabled_vif);
/* Scanning */
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
@ -854,15 +885,24 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
struct ieee80211_scan_ies *ies);
int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/* Unified scan */
int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *req);
int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_scan_ies *ies);
/* MVM debugfs */
#ifdef CONFIG_IWLWIFI_DEBUGFS
@ -963,11 +1003,30 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
enum ieee80211_band band);
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac);
bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event);
u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
enum ieee80211_band band);
int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
enum iwl_bt_kill_msk {
BT_KILL_MSK_DEFAULT,
BT_KILL_MSK_SCO_HID_A2DP,
@ -1039,4 +1098,9 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool added_vif);
/* TDLS */
int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
#endif /* __IWL_MVM_H__ */

Some files were not shown because too many files have changed in this diff Show more