提交 57219dc7 编写于 作者: D David S. Miller

Merge tag 'master-2014-09-16' of...

Merge tag 'master-2014-09-16' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next

John W. Linville says:

====================
pull request: wireless-next 2014-09-22

Please pull this batch of updates intended for the 3.18 stream...

For the mac80211 bits, Johannes says:

"This time, I have some rate minstrel improvements, support for a very
small feature from CCX that Steinar reverse-engineered, dynamic ACK
timeout support, a number of changes for TDLS, early support for radio
resource measurement and many fixes. Also, I'm changing a number of
places to clear key memory when it's freed and Intel claims copyright
for code they developed."

For the bluetooth bits, Johan says:

"Here are some more patches intended for 3.18. Most of them are cleanups
or fixes for SMP. The only exception is a fix for BR/EDR L2CAP fixed
channels which should now work better together with the L2CAP
information request procedure."

For the iwlwifi bits, Emmanuel says:

"I fix here dvm which was broken by my last pull request. Arik
continues to work on TDLS and Luca solved a few issues in CT-Kill. Eyal
keeps digging into rate scaling code, more to come soon. Besides this,
nothing really special here."

Beyond that, there are the usual big batches of updates to ath9k, b43,
mwifiex, and wil6210 as well as a handful of other bits here and there.
Also, rtlwifi gets some btcoexist attention from Larry.

Please let me know if there are problems!
====================

Had to adjust the wil6210 code to comply with Joe Perches's recent
change in net-next to make the netdev_*() routines return void instead
of 'int'.
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
......@@ -210,6 +210,10 @@ static void __init bcm47xx_register_bcma(void)
pr_warn("bcm47xx: someone else already registered a bcma SPROM callback handler (err %d)\n", err);
err = bcma_host_soc_register(&bcm47xx_bus.bcma);
if (err)
panic("Failed to register BCMA bus (err %d)", err);
err = bcma_host_soc_init(&bcm47xx_bus.bcma);
if (err)
panic("Failed to initialize BCMA bus (err %d)", err);
......
bcma-y += main.o scan.o core.o sprom.o
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
bcma-y += driver_chipcommon_b.o
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
bcma-y += driver_pci.o
......
......@@ -50,6 +50,10 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
extern struct platform_device bcma_pflash_dev;
#endif /* CONFIG_BCMA_DRIVER_MIPS */
/* driver_chipcommon_b.c */
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb);
void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb);
/* driver_chipcommon_pmu.c */
u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
......
/*
* Broadcom specific AMBA
* ChipCommon B Unit driver
*
* Copyright 2014, Hauke Mehrtens <hauke@hauke-m.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "bcma_private.h"
#include <linux/export.h>
#include <linux/bcma/bcma.h>
static bool bcma_wait_reg(struct bcma_bus *bus, void __iomem *addr, u32 mask,
u32 value, int timeout)
{
unsigned long deadline = jiffies + timeout;
u32 val;
do {
val = readl(addr);
if ((val & mask) == value)
return true;
cpu_relax();
udelay(10);
} while (!time_after_eq(jiffies, deadline));
bcma_err(bus, "Timeout waiting for register %p\n", addr);
return false;
}
void bcma_chipco_b_mii_write(struct bcma_drv_cc_b *ccb, u32 offset, u32 value)
{
struct bcma_bus *bus = ccb->core->bus;
writel(offset, ccb->mii + 0x00);
bcma_wait_reg(bus, ccb->mii + 0x00, 0x0100, 0x0000, 100);
writel(value, ccb->mii + 0x04);
bcma_wait_reg(bus, ccb->mii + 0x00, 0x0100, 0x0000, 100);
}
EXPORT_SYMBOL_GPL(bcma_chipco_b_mii_write);
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb)
{
if (ccb->setup_done)
return 0;
ccb->setup_done = 1;
ccb->mii = ioremap_nocache(ccb->core->addr_s[1], BCMA_CORE_SIZE);
if (!ccb->mii)
return -ENOMEM;
return 0;
}
void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb)
{
if (ccb->mii)
iounmap(ccb->mii);
}
......@@ -208,6 +208,9 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
bus->boardinfo.vendor = bus->host_pci->subsystem_vendor;
bus->boardinfo.type = bus->host_pci->subsystem_device;
/* Initialize struct, detect chip */
bcma_init_bus(bus);
/* Register */
err = bcma_bus_register(bus);
if (err)
......
......@@ -165,7 +165,6 @@ static const struct bcma_host_ops bcma_host_soc_ops = {
int __init bcma_host_soc_register(struct bcma_soc *soc)
{
struct bcma_bus *bus = &soc->bus;
int err;
/* iomap only first core. We have to read some register on this core
* to scan the bus.
......@@ -178,7 +177,18 @@ int __init bcma_host_soc_register(struct bcma_soc *soc)
bus->hosttype = BCMA_HOSTTYPE_SOC;
bus->ops = &bcma_host_soc_ops;
/* Register */
/* Initialize struct, detect chip */
bcma_init_bus(bus);
return 0;
}
int __init bcma_host_soc_init(struct bcma_soc *soc)
{
struct bcma_bus *bus = &soc->bus;
int err;
/* Scan bus and initialize it */
err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
if (err)
iounmap(bus->mmio);
......
......@@ -120,16 +120,60 @@ static void bcma_release_core_dev(struct device *dev)
kfree(core);
}
static int bcma_register_cores(struct bcma_bus *bus)
static bool bcma_is_core_needed_early(u16 core_id)
{
switch (core_id) {
case BCMA_CORE_NS_NAND:
case BCMA_CORE_NS_QSPI:
return true;
}
return false;
}
static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
{
int err;
core->dev.release = bcma_release_core_dev;
core->dev.bus = &bcma_bus_type;
dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
switch (bus->hosttype) {
case BCMA_HOSTTYPE_PCI:
core->dev.parent = &bus->host_pci->dev;
core->dma_dev = &bus->host_pci->dev;
core->irq = bus->host_pci->irq;
break;
case BCMA_HOSTTYPE_SOC:
core->dev.dma_mask = &core->dev.coherent_dma_mask;
core->dma_dev = &core->dev;
break;
case BCMA_HOSTTYPE_SDIO:
break;
}
err = device_register(&core->dev);
if (err) {
bcma_err(bus, "Could not register dev for core 0x%03X\n",
core->id.id);
put_device(&core->dev);
return;
}
core->dev_registered = true;
}
static int bcma_register_devices(struct bcma_bus *bus)
{
struct bcma_device *core;
int err, dev_id = 0;
int err;
list_for_each_entry(core, &bus->cores, list) {
/* We support that cores ourself */
switch (core->id.id) {
case BCMA_CORE_4706_CHIPCOMMON:
case BCMA_CORE_CHIPCOMMON:
case BCMA_CORE_NS_CHIPCOMMON_B:
case BCMA_CORE_PCI:
case BCMA_CORE_PCIE:
case BCMA_CORE_PCIE2:
......@@ -138,39 +182,16 @@ static int bcma_register_cores(struct bcma_bus *bus)
continue;
}
/* Early cores were already registered */
if (bcma_is_core_needed_early(core->id.id))
continue;
/* Only first GMAC core on BCM4706 is connected and working */
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
core->core_unit > 0)
continue;
core->dev.release = bcma_release_core_dev;
core->dev.bus = &bcma_bus_type;
dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id);
switch (bus->hosttype) {
case BCMA_HOSTTYPE_PCI:
core->dev.parent = &bus->host_pci->dev;
core->dma_dev = &bus->host_pci->dev;
core->irq = bus->host_pci->irq;
break;
case BCMA_HOSTTYPE_SOC:
core->dev.dma_mask = &core->dev.coherent_dma_mask;
core->dma_dev = &core->dev;
break;
case BCMA_HOSTTYPE_SDIO:
break;
}
err = device_register(&core->dev);
if (err) {
bcma_err(bus,
"Could not register dev for core 0x%03X\n",
core->id.id);
put_device(&core->dev);
continue;
}
core->dev_registered = true;
dev_id++;
bcma_register_core(bus, core);
}
#ifdef CONFIG_BCMA_DRIVER_MIPS
......@@ -247,6 +268,12 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_chipcommon_early_init(&bus->drv_cc);
}
/* Cores providing flash access go before SPROM init */
list_for_each_entry(core, &bus->cores, list) {
if (bcma_is_core_needed_early(core->id.id))
bcma_register_core(bus, core);
}
/* Try to get SPROM */
err = bcma_sprom_get(bus);
if (err == -ENOENT) {
......@@ -261,6 +288,13 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_chipcommon_init(&bus->drv_cc);
}
/* Init CC core */
core = bcma_find_core(bus, BCMA_CORE_NS_CHIPCOMMON_B);
if (core) {
bus->drv_cc_b.core = core;
bcma_core_chipcommon_b_init(&bus->drv_cc_b);
}
/* Init MIPS core */
core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
if (core) {
......@@ -297,7 +331,7 @@ int bcma_bus_register(struct bcma_bus *bus)
}
/* Register found cores */
bcma_register_cores(bus);
bcma_register_devices(bus);
bcma_info(bus, "Bus registered\n");
......@@ -315,6 +349,8 @@ void bcma_bus_unregister(struct bcma_bus *bus)
else if (err)
bcma_err(bus, "Can not unregister GPIO driver: %i\n", err);
bcma_core_chipcommon_b_free(&bus->drv_cc_b);
cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE);
cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
......@@ -334,8 +370,6 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
struct bcma_device *core;
struct bcma_device_id match;
bcma_init_bus(bus);
match.manuf = BCMA_MANUF_BCM;
match.id = bcma_cc_core_id(bus);
match.class = BCMA_CL_SIM;
......
......@@ -276,7 +276,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
struct bcma_device *core)
{
u32 tmp;
u8 i, j;
u8 i, j, k;
s32 cia, cib;
u8 ports[2], wrappers[2];
......@@ -314,6 +314,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
/* Some specific cores don't need wrappers */
switch (core->id.id) {
case BCMA_CORE_4706_MAC_GBIT_COMMON:
case BCMA_CORE_NS_CHIPCOMMON_B:
/* Not used yet: case BCMA_CORE_OOB_ROUTER: */
break;
default:
......@@ -367,6 +368,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
core->addr = tmp;
/* get & parse slave ports */
k = 0;
for (i = 0; i < ports[1]; i++) {
for (j = 0; ; j++) {
tmp = bcma_erom_get_addr_desc(bus, eromptr,
......@@ -376,9 +378,9 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
/* pr_debug("erom: slave port %d "
* "has %d descriptors\n", i, j); */
break;
} else {
if (i == 0 && j == 0)
core->addr1 = tmp;
} else if (k < ARRAY_SIZE(core->addr_s)) {
core->addr_s[k] = tmp;
k++;
}
}
}
......@@ -438,9 +440,6 @@ void bcma_init_bus(struct bcma_bus *bus)
s32 tmp;
struct bcma_chipinfo *chipinfo = &(bus->chipinfo);
if (bus->init_done)
return;
INIT_LIST_HEAD(&bus->cores);
bus->nr_cores = 0;
......@@ -452,8 +451,6 @@ void bcma_init_bus(struct bcma_bus *bus)
chipinfo->pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
bcma_info(bus, "Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
chipinfo->id, chipinfo->rev, chipinfo->pkg);
bus->init_done = true;
}
int bcma_bus_scan(struct bcma_bus *bus)
......@@ -463,8 +460,6 @@ int bcma_bus_scan(struct bcma_bus *bus)
int err, core_num = 0;
bcma_init_bus(bus);
erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
......
......@@ -331,6 +331,9 @@ static void btusb_intr_complete(struct urb *urb)
BT_ERR("%s corrupted event packet", hdev->name);
hdev->stat.err_rx++;
}
} else if (urb->status == -ENOENT) {
/* Avoid suspend failed when usb_kill_urb */
return;
}
if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
......@@ -419,6 +422,9 @@ static void btusb_bulk_complete(struct urb *urb)
BT_ERR("%s corrupted ACL packet", hdev->name);
hdev->stat.err_rx++;
}
} else if (urb->status == -ENOENT) {
/* Avoid suspend failed when usb_kill_urb */
return;
}
if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
......@@ -513,6 +519,9 @@ static void btusb_isoc_complete(struct urb *urb)
hdev->stat.err_rx++;
}
}
} else if (urb->status == -ENOENT) {
/* Avoid suspend failed when usb_kill_urb */
return;
}
if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
......
......@@ -234,6 +234,7 @@ void ath_printk(const char *level, const struct ath_common *common,
* AR9462.
* @ATH_DBG_DFS: radar datection
* @ATH_DBG_WOW: Wake on Wireless
* @ATH_DBG_DYNACK: dynack handling
* @ATH_DBG_ANY: enable all debugging
*
* The debug level is used to control the amount and type of debugging output
......@@ -262,6 +263,7 @@ enum ATH_DEBUG {
ATH_DBG_DFS = 0x00010000,
ATH_DBG_WOW = 0x00020000,
ATH_DBG_CHAN_CTX = 0x00040000,
ATH_DBG_DYNACK = 0x00080000,
ATH_DBG_ANY = 0xffffffff
};
......
......@@ -4838,7 +4838,6 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_SPECTRUM_MGMT;
......@@ -4846,8 +4845,10 @@ int ath10k_mac_register(struct ath10k *ar)
* bytes is used for padding/alignment if necessary. */
ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
......
......@@ -704,7 +704,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
* reset.
*/
static void
ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
ath5k_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
{
struct ath5k_hw *ah = hw->priv;
......
......@@ -92,6 +92,15 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything
except increase code size.
config ATH9K_DYNACK
bool "Atheros ath9k ACK timeout estimation algorithm (EXPERIMENTAL)"
depends on ATH9K
default n
---help---
This option enables ath9k dynamic ACK timeout estimation algorithm
based on ACK frame RX timestamp, TX frame timestamp and frame
duration
config ATH9K_TX99
bool "Atheros ath9k TX99 testing support"
depends on ATH9K_DEBUGFS && CFG80211_CERTIFICATION_ONUS
......
......@@ -49,6 +49,9 @@ ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o
ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
ar9003_mci.o
ath9k_hw-$(CONFIG_ATH9K_DYNACK) += dynack.o
obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
......
......@@ -381,6 +381,13 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
ts->evm1 = ads->AR_TxEVM1;
ts->evm2 = ads->AR_TxEVM2;
status = ACCESS_ONCE(ads->ds_ctl4);
ts->duration[0] = MS(status, AR_PacketDur0);
ts->duration[1] = MS(status, AR_PacketDur1);
status = ACCESS_ONCE(ads->ds_ctl5);
ts->duration[2] = MS(status, AR_PacketDur2);
ts->duration[3] = MS(status, AR_PacketDur3);
return 0;
}
......
......@@ -355,9 +355,11 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
struct ath_tx_status *ts)
{
struct ar9003_txs *ads;
struct ar9003_txc *adc;
u32 status;
ads = &ah->ts_ring[ah->ts_tail];
adc = (struct ar9003_txc *)ads;
status = ACCESS_ONCE(ads->status8);
if ((status & AR_TxDone) == 0)
......@@ -426,6 +428,13 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
status = ACCESS_ONCE(adc->ctl15);
ts->duration[0] = MS(status, AR_PacketDur0);
ts->duration[1] = MS(status, AR_PacketDur1);
status = ACCESS_ONCE(adc->ctl16);
ts->duration[2] = MS(status, AR_PacketDur2);
ts->duration[3] = MS(status, AR_PacketDur3);
memset(ads, 0, sizeof(*ads));
return 0;
......
......@@ -274,6 +274,9 @@ struct ath_node {
struct ath_rx_rate_stats rx_rate_stats;
#endif
u8 key_idx[4];
u32 ackto;
struct list_head list;
};
struct ath_tx_control {
......@@ -314,7 +317,6 @@ struct ath_rx {
bool discard_next;
u32 *rxlink;
u32 num_pkts;
unsigned int rxfilter;
struct list_head rxbuf;
struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
......@@ -350,6 +352,9 @@ struct ath_chanctx {
bool active;
bool assigned;
bool switch_after_beacon;
short nvifs;
unsigned int rxfilter;
};
enum ath_chanctx_event {
......@@ -376,6 +381,9 @@ enum ath_chanctx_state {
struct ath_chanctx_sched {
bool beacon_pending;
bool offchannel_pending;
bool wait_switch;
bool force_noa_update;
bool extend_absence;
enum ath_chanctx_state state;
u8 beacon_miss;
......@@ -449,7 +457,7 @@ void ath9k_p2p_ps_timer(void *priv);
void ath9k_chanctx_wake_queues(struct ath_softc *sc);
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
enum ath_chanctx_event ev);
void ath_chanctx_beacon_sent_ev(struct ath_softc *sc,
enum ath_chanctx_event ev);
......@@ -478,7 +486,7 @@ static inline void ath9k_offchannel_init(struct ath_softc *sc)
static inline void ath9k_deinit_channel_context(struct ath_softc *sc)
{
}
static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
enum ath_chanctx_event ev)
{
}
......@@ -527,7 +535,7 @@ static inline void ath_chanctx_check_active(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
void ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
int ath_rx_init(struct ath_softc *sc, int nbufs);
......@@ -572,6 +580,8 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
/* VIFs */
/********/
#define P2P_DEFAULT_CTWIN 10
struct ath_vif {
struct list_head list;
......@@ -590,8 +600,10 @@ struct ath_vif {
u32 offchannel_start;
u32 offchannel_duration;
u32 periodic_noa_start;
u32 periodic_noa_duration;
/* These are used for both periodic and one-shot */
u32 noa_start;
u32 noa_duration;
bool periodic_noa;
};
struct ath9k_vif_iter_data {
......@@ -960,7 +972,6 @@ struct ath_softc {
bool ps_enabled;
bool ps_idle;
short nbcnvifs;
short nvifs;
unsigned long ps_usecount;
struct ath_rx rx;
......
......@@ -183,7 +183,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
spin_unlock_bh(&cabq->axq_lock);
if (skb && cabq_depth) {
if (sc->nvifs > 1) {
if (sc->cur_chan->nvifs > 1) {
ath_dbg(common, BEACON,
"Flushing previous cabq traffic\n");
ath_draintxq(sc, cabq);
......@@ -514,6 +514,18 @@ 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 (ath9k_is_chanctx_enabled()) {
/*
* If the VIF is not present in the current channel context,
* then we can't do the usual opmode checks. Allow the
* beacon config for the VIF to be updated in this case and
* return immediately.
*/
if (sc->cur_chan != avp->chanctx)
return true;
}
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
if ((vif->type != NL80211_IFTYPE_AP) ||
......
......@@ -83,8 +83,6 @@ static int ath_set_channel(struct ath_softc *sc)
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;
......@@ -262,6 +260,9 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
cur = sc->cur_chan;
prev = ath_chanctx_get_next(sc, cur);
if (!prev->switch_after_beacon)
return;
getrawmonotonic(&ts);
cur_tsf = (u32) cur->tsf_val +
ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
......@@ -310,7 +311,6 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_chanctx *ctx;
u32 tsf_time;
u32 beacon_int;
bool noa_changed = false;
if (vif)
avp = (struct ath_vif *) vif->drv_priv;
......@@ -333,7 +333,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
break;
}
if (sc->sched.offchannel_pending) {
if (sc->sched.offchannel_pending && !sc->sched.wait_switch) {
sc->sched.offchannel_pending = false;
sc->next_chan = &sc->offchannel.chan;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
......@@ -372,44 +372,91 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
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 an offchannel switch is scheduled to happen after
* a beacon transmission, update the NoA with one-shot
* values and increment the index.
*/
if (sc->next_chan == &sc->offchannel.chan) {
avp->noa_index++;
avp->offchannel_start = tsf_time;
avp->offchannel_duration = sc->sched.offchannel_duration;
ath_dbg(common, CHAN_CTX,
"offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
avp->offchannel_duration,
avp->offchannel_start,
avp->noa_index);
/*
* When multiple contexts are active, the NoA
* has to be recalculated and advertised after
* an offchannel operation.
*/
if (ctx->active && avp->noa_duration)
avp->noa_duration = 0;
break;
}
/*
* Clear the extend_absence flag if it had been
* set during the previous beacon transmission,
* since we need to revert to the normal NoA
* schedule.
*/
if (ctx->active && sc->sched.extend_absence) {
avp->noa_duration = 0;
sc->sched.extend_absence = false;
}
/* 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 (ctx->active && sc->sched.beacon_miss >= 2) {
avp->noa_duration = 0;
sc->sched.extend_absence = true;
}
if (noa_changed)
/* Prevent wrap-around issues */
if (avp->noa_duration && tsf_time - avp->noa_start > BIT(30))
avp->noa_duration = 0;
/*
* If multiple contexts are active, start periodic
* NoA and increment the index for the first
* announcement.
*/
if (ctx->active &&
(!avp->noa_duration || sc->sched.force_noa_update)) {
avp->noa_index++;
avp->noa_start = tsf_time;
if (sc->sched.extend_absence)
avp->noa_duration = (3 * beacon_int / 2) +
sc->sched.channel_switch_time;
else
avp->noa_duration =
TU_TO_USEC(cur_conf->beacon_interval) / 2 +
sc->sched.channel_switch_time;
if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
sc->sched.extend_absence)
avp->periodic_noa = false;
else
avp->periodic_noa = true;
ath_dbg(common, CHAN_CTX,
"periodic_noa_duration: %d, periodic_noa_start: %d, noa_index: %d\n",
avp->periodic_noa_duration,
avp->periodic_noa_start,
avp->noa_index);
ath_dbg(common, CHAN_CTX,
"noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
avp->noa_duration,
avp->noa_start,
avp->noa_index,
avp->periodic_noa);
}
if (ctx->active && sc->sched.force_noa_update)
sc->sched.force_noa_update = false;
break;
case ATH_CHANCTX_EVENT_BEACON_SENT:
......@@ -490,9 +537,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
"Move chanctx state to WAIT_FOR_TIMER (event SWITCH)\n");
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
sc->sched.wait_switch = false;
tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
if (sc->sched.beacon_miss >= 2) {
if (sc->sched.extend_absence) {
sc->sched.beacon_miss = 0;
tsf_time *= 3;
}
......@@ -560,10 +609,9 @@ void ath_chanctx_beacon_sent_ev(struct ath_softc *sc,
ath_chanctx_event(sc, NULL, ev);
}
void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts,
void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
enum ath_chanctx_event ev)
{
sc->sched.next_tbtt = ts;
ath_chanctx_event(sc, NULL, ev);
}
......@@ -587,8 +635,18 @@ static void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
(sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
if (chandef)
ctx->chandef = *chandef;
sc->sched.offchannel_pending = true;
sc->sched.wait_switch = true;
sc->sched.offchannel_duration =
jiffies_to_usecs(sc->offchannel.duration) +
sc->sched.channel_switch_time;
spin_unlock_bh(&sc->chan_lock);
ath_dbg(common, CHAN_CTX,
"Set offchannel_pending to true\n");
return;
}
......@@ -601,7 +659,7 @@ static void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
if (sc->next_chan == &sc->offchannel.chan) {
sc->sched.offchannel_duration =
TU_TO_USEC(sc->offchannel.duration) +
jiffies_to_usecs(sc->offchannel.duration) +
sc->sched.channel_switch_time;
if (chandef) {
......@@ -688,7 +746,8 @@ void ath_offchannel_next(struct ath_softc *sc)
} else if (sc->offchannel.roc_vif) {
vif = sc->offchannel.roc_vif;
sc->offchannel.chan.txpower = vif->bss_conf.txpower;
sc->offchannel.duration = sc->offchannel.roc_duration;
sc->offchannel.duration =
msecs_to_jiffies(sc->offchannel.roc_duration);
sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
} else {
......@@ -724,6 +783,10 @@ void ath_scan_complete(struct ath_softc *sc, bool abort)
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
ieee80211_scan_completed(sc->hw, abort);
clear_bit(ATH_OP_SCANNING, &common->op_flags);
spin_lock_bh(&sc->chan_lock);
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
sc->sched.force_noa_update = true;
spin_unlock_bh(&sc->chan_lock);
ath_offchannel_next(sc);
ath9k_ps_restore(sc);
}
......@@ -959,8 +1022,8 @@ static void ath_offchannel_channel_change(struct ath_softc *sc)
break;
sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
mod_timer(&sc->offchannel.timer, jiffies +
msecs_to_jiffies(sc->offchannel.duration));
mod_timer(&sc->offchannel.timer,
jiffies + sc->offchannel.duration);
ieee80211_ready_on_channel(sc->hw);
break;
case ATH_OFFCHANNEL_ROC_DONE:
......@@ -1022,7 +1085,10 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force)
sc->cur_chan = sc->next_chan;
sc->cur_chan->stopped = false;
sc->next_chan = NULL;
sc->sched.offchannel_duration = 0;
if (!sc->sched.offchannel_pending)
sc->sched.offchannel_duration = 0;
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
......@@ -1165,6 +1231,30 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
ath9k_update_p2p_ps_timer(sc, avp);
}
static u8 ath9k_get_ctwin(struct ath_softc *sc, struct ath_vif *avp)
{
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
u8 switch_time, ctwin;
/*
* Channel switch in multi-channel mode is deferred
* by a quarter beacon interval when handling
* ATH_CHANCTX_EVENT_BEACON_PREPARE, so the P2P-GO
* interface is guaranteed to be discoverable
* for that duration after a TBTT.
*/
switch_time = cur_conf->beacon_interval / 4;
ctwin = avp->vif->bss_conf.p2p_noa_attr.oppps_ctwindow;
if (ctwin && (ctwin < switch_time))
return ctwin;
if (switch_time < P2P_DEFAULT_CTWIN)
return 0;
return P2P_DEFAULT_CTWIN;
}
void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
struct sk_buff *skb)
{
......@@ -1182,10 +1272,10 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
int noa_len, noa_desc, i = 0;
u8 *hdr;
if (!avp->offchannel_duration && !avp->periodic_noa_duration)
if (!avp->offchannel_duration && !avp->noa_duration)
return;
noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
noa_desc = !!avp->offchannel_duration + !!avp->noa_duration;
noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
hdr = skb_put(skb, sizeof(noa_ie_hdr));
......@@ -1197,13 +1287,19 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
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->oppps_ctwindow = ath9k_get_ctwin(sc, avp);
if (avp->noa_duration) {
if (avp->periodic_noa) {
u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
noa->desc[i].count = 255;
noa->desc[i].interval = cpu_to_le32(interval);
} else {
noa->desc[i].count = 1;
}
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);
noa->desc[i].start_time = cpu_to_le32(avp->noa_start);
noa->desc[i].duration = cpu_to_le32(avp->noa_duration);
i++;
}
......
......@@ -838,7 +838,7 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
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);
iter_data.nadhocs, sc->cur_chan->nvifs, sc->nbcnvifs);
}
if (len > sizeof(buf))
......@@ -1169,6 +1169,29 @@ static const struct file_operations fops_btcoex = {
};
#endif
#ifdef CONFIG_ATH9K_DYNACK
static ssize_t read_file_ackto(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
char buf[32];
unsigned int len;
len = sprintf(buf, "%u %c\n", ah->dynack.ackto,
(ah->dynack.enabled) ? 'A' : 'S');
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_ackto = {
.read = read_file_ackto,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#endif
/* Ethtool support for get-stats */
#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO"
......@@ -1374,5 +1397,10 @@ int ath9k_init_debug(struct ath_hw *ah)
&fops_btcoex);
#endif
#ifdef CONFIG_ATH9K_DYNACK
debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_ackto);
#endif
return 0;
}
/*
* Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*
* 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"
#include "hw.h"
#include "dynack.h"
#define COMPUTE_TO (5 * HZ)
#define LATEACK_DELAY (10 * HZ)
#define LATEACK_TO 256
#define MAX_DELAY 300
#define EWMA_LEVEL 96
#define EWMA_DIV 128
/**
* ath_dynack_ewma - EWMA (Exponentially Weighted Moving Average) calculation
*
*/
static inline u32 ath_dynack_ewma(u32 old, u32 new)
{
return (new * (EWMA_DIV - EWMA_LEVEL) + old * EWMA_LEVEL) / EWMA_DIV;
}
/**
* ath_dynack_get_sifs - get sifs time based on phy used
* @ah: ath hw
* @phy: phy used
*
*/
static inline u32 ath_dynack_get_sifs(struct ath_hw *ah, int phy)
{
u32 sifs = CCK_SIFS_TIME;
if (phy == WLAN_RC_PHY_OFDM) {
if (IS_CHAN_QUARTER_RATE(ah->curchan))
sifs = OFDM_SIFS_TIME_QUARTER;
else if (IS_CHAN_HALF_RATE(ah->curchan))
sifs = OFDM_SIFS_TIME_HALF;
else
sifs = OFDM_SIFS_TIME;
}
return sifs;
}
/**
* ath_dynack_bssidmask - filter out ACK frames based on BSSID mask
* @ah: ath hw
* @mac: receiver address
*/
static inline bool ath_dynack_bssidmask(struct ath_hw *ah, const u8 *mac)
{
int i;
struct ath_common *common = ath9k_hw_common(ah);
for (i = 0; i < ETH_ALEN; i++) {
if ((common->macaddr[i] & common->bssidmask[i]) !=
(mac[i] & common->bssidmask[i]))
return false;
}
return true;
}
/**
* ath_dynack_compute_ackto - compute ACK timeout as the maximum STA timeout
* @ah: ath hw
*
* should be called while holding qlock
*/
static void ath_dynack_compute_ackto(struct ath_hw *ah)
{
struct ath_node *an;
u32 to = 0;
struct ath_dynack *da = &ah->dynack;
struct ath_common *common = ath9k_hw_common(ah);
list_for_each_entry(an, &da->nodes, list)
if (an->ackto > to)
to = an->ackto;
if (to && da->ackto != to) {
u32 slottime;
slottime = (to - 3) / 2;
da->ackto = to;
ath_dbg(common, DYNACK, "ACK timeout %u slottime %u\n",
da->ackto, slottime);
ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, da->ackto);
ath9k_hw_set_cts_timeout(ah, da->ackto);
}
}
/**
* ath_dynack_compute_to - compute STA ACK timeout
* @ah: ath hw
*
* should be called while holding qlock
*/
static void ath_dynack_compute_to(struct ath_hw *ah)
{
u32 ackto, ack_ts;
u8 *dst, *src;
struct ieee80211_sta *sta;
struct ath_node *an;
struct ts_info *st_ts;
struct ath_dynack *da = &ah->dynack;
rcu_read_lock();
while (da->st_rbf.h_rb != da->st_rbf.t_rb &&
da->ack_rbf.h_rb != da->ack_rbf.t_rb) {
ack_ts = da->ack_rbf.tstamp[da->ack_rbf.h_rb];
st_ts = &da->st_rbf.ts[da->st_rbf.h_rb];
dst = da->st_rbf.addr[da->st_rbf.h_rb].h_dest;
src = da->st_rbf.addr[da->st_rbf.h_rb].h_src;
ath_dbg(ath9k_hw_common(ah), DYNACK,
"ack_ts %u st_ts %u st_dur %u [%u-%u]\n",
ack_ts, st_ts->tstamp, st_ts->dur,
da->ack_rbf.h_rb, da->st_rbf.h_rb);
if (ack_ts > st_ts->tstamp + st_ts->dur) {
ackto = ack_ts - st_ts->tstamp - st_ts->dur;
if (ackto < MAX_DELAY) {
sta = ieee80211_find_sta_by_ifaddr(ah->hw, dst,
src);
if (sta) {
an = (struct ath_node *)sta->drv_priv;
an->ackto = ath_dynack_ewma(an->ackto,
ackto);
ath_dbg(ath9k_hw_common(ah), DYNACK,
"%pM to %u\n", dst, an->ackto);
if (time_is_before_jiffies(da->lto)) {
ath_dynack_compute_ackto(ah);
da->lto = jiffies + COMPUTE_TO;
}
}
INCR(da->ack_rbf.h_rb, ATH_DYN_BUF);
}
INCR(da->st_rbf.h_rb, ATH_DYN_BUF);
} else {
INCR(da->ack_rbf.h_rb, ATH_DYN_BUF);
}
}
rcu_read_unlock();
}
/**
* ath_dynack_sample_tx_ts - status timestamp sampling method
* @ah: ath hw
* @skb: socket buffer
* @ts: tx status info
*
*/
void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
struct ath_tx_status *ts)
{
u8 ridx;
struct ieee80211_hdr *hdr;
struct ath_dynack *da = &ah->dynack;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !da->enabled)
return;
spin_lock_bh(&da->qlock);
hdr = (struct ieee80211_hdr *)skb->data;
/* late ACK */
if (ts->ts_status & ATH9K_TXERR_XRETRY) {
if (ieee80211_is_assoc_req(hdr->frame_control) ||
ieee80211_is_assoc_resp(hdr->frame_control)) {
ath_dbg(common, DYNACK, "late ack\n");
ath9k_hw_setslottime(ah, (LATEACK_TO - 3) / 2);
ath9k_hw_set_ack_timeout(ah, LATEACK_TO);
ath9k_hw_set_cts_timeout(ah, LATEACK_TO);
da->lto = jiffies + LATEACK_DELAY;
}
spin_unlock_bh(&da->qlock);
return;
}
ridx = ts->ts_rateindex;
da->st_rbf.ts[da->st_rbf.t_rb].tstamp = ts->ts_tstamp;
da->st_rbf.ts[da->st_rbf.t_rb].dur = ts->duration[ts->ts_rateindex];
ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_dest, hdr->addr1);
ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_src, hdr->addr2);
if (!(info->status.rates[ridx].flags & IEEE80211_TX_RC_MCS)) {
u32 phy, sifs;
const struct ieee80211_rate *rate;
struct ieee80211_tx_rate *rates = info->status.rates;
rate = &common->sbands[info->band].bitrates[rates[ridx].idx];
if (info->band == IEEE80211_BAND_2GHZ &&
!(rate->flags & IEEE80211_RATE_ERP_G))
phy = WLAN_RC_PHY_CCK;
else
phy = WLAN_RC_PHY_OFDM;
sifs = ath_dynack_get_sifs(ah, phy);
da->st_rbf.ts[da->st_rbf.t_rb].dur -= sifs;
}
ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n",
hdr->addr1, da->st_rbf.ts[da->st_rbf.t_rb].tstamp,
da->st_rbf.ts[da->st_rbf.t_rb].dur, da->st_rbf.h_rb,
(da->st_rbf.t_rb + 1) % ATH_DYN_BUF);
INCR(da->st_rbf.t_rb, ATH_DYN_BUF);
if (da->st_rbf.t_rb == da->st_rbf.h_rb)
INCR(da->st_rbf.h_rb, ATH_DYN_BUF);
ath_dynack_compute_to(ah);
spin_unlock_bh(&da->qlock);
}
EXPORT_SYMBOL(ath_dynack_sample_tx_ts);
/**
* ath_dynack_sample_ack_ts - ACK timestamp sampling method
* @ah: ath hw
* @skb: socket buffer
* @ts: rx timestamp
*
*/
void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb,
u32 ts)
{
struct ath_dynack *da = &ah->dynack;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ath_dynack_bssidmask(ah, hdr->addr1) || !da->enabled)
return;
spin_lock_bh(&da->qlock);
da->ack_rbf.tstamp[da->ack_rbf.t_rb] = ts;
ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n",
da->ack_rbf.tstamp[da->ack_rbf.t_rb],
da->ack_rbf.h_rb, (da->ack_rbf.t_rb + 1) % ATH_DYN_BUF);
INCR(da->ack_rbf.t_rb, ATH_DYN_BUF);
if (da->ack_rbf.t_rb == da->ack_rbf.h_rb)
INCR(da->ack_rbf.h_rb, ATH_DYN_BUF);
ath_dynack_compute_to(ah);
spin_unlock_bh(&da->qlock);
}
EXPORT_SYMBOL(ath_dynack_sample_ack_ts);
/**
* ath_dynack_node_init - init ath_node related info
* @ah: ath hw
* @an: ath node
*
*/
void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an)
{
/* ackto = slottime + sifs + air delay */
u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64;
struct ath_dynack *da = &ah->dynack;
an->ackto = ackto;
spin_lock(&da->qlock);
list_add_tail(&an->list, &da->nodes);
spin_unlock(&da->qlock);
}
EXPORT_SYMBOL(ath_dynack_node_init);
/**
* ath_dynack_node_deinit - deinit ath_node related info
* @ah: ath hw
* @an: ath node
*
*/
void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an)
{
struct ath_dynack *da = &ah->dynack;
spin_lock(&da->qlock);
list_del(&an->list);
spin_unlock(&da->qlock);
}
EXPORT_SYMBOL(ath_dynack_node_deinit);
/**
* ath_dynack_reset - reset dynack processing
* @ah: ath hw
*
*/
void ath_dynack_reset(struct ath_hw *ah)
{
/* ackto = slottime + sifs + air delay */
u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64;
struct ath_dynack *da = &ah->dynack;
da->lto = jiffies;
da->ackto = ackto;
da->st_rbf.t_rb = 0;
da->st_rbf.h_rb = 0;
da->ack_rbf.t_rb = 0;
da->ack_rbf.h_rb = 0;
/* init acktimeout */
ath9k_hw_setslottime(ah, (ackto - 3) / 2);
ath9k_hw_set_ack_timeout(ah, ackto);
ath9k_hw_set_cts_timeout(ah, ackto);
}
EXPORT_SYMBOL(ath_dynack_reset);
/**
* ath_dynack_init - init dynack data structure
* @ah: ath hw
*
*/
void ath_dynack_init(struct ath_hw *ah)
{
struct ath_dynack *da = &ah->dynack;
memset(da, 0, sizeof(struct ath_dynack));
spin_lock_init(&da->qlock);
INIT_LIST_HEAD(&da->nodes);
ah->hw->wiphy->features |= NL80211_FEATURE_ACKTO_ESTIMATION;
}
/*
* Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*
* 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 DYNACK_H
#define DYNACK_H
#define ATH_DYN_BUF 64
struct ath_hw;
struct ath_node;
/**
* struct ath_dyn_rxbuf - ACK frame ring buffer
* @h_rb: ring buffer head
* @t_rb: ring buffer tail
* @tstamp: ACK RX timestamp buffer
*/
struct ath_dyn_rxbuf {
u16 h_rb, t_rb;
u32 tstamp[ATH_DYN_BUF];
};
struct ts_info {
u32 tstamp;
u32 dur;
};
struct haddr_pair {
u8 h_dest[ETH_ALEN];
u8 h_src[ETH_ALEN];
};
/**
* struct ath_dyn_txbuf - tx frame ring buffer
* @h_rb: ring buffer head
* @t_rb: ring buffer tail
* @addr: dest/src address pair for a given TX frame
* @ts: TX frame timestamp buffer
*/
struct ath_dyn_txbuf {
u16 h_rb, t_rb;
struct haddr_pair addr[ATH_DYN_BUF];
struct ts_info ts[ATH_DYN_BUF];
};
/**
* struct ath_dynack - dynack processing info
* @enabled: enable dyn ack processing
* @ackto: current ACK timeout
* @lto: last ACK timeout computation
* @nodes: ath_node linked list
* @qlock: ts queue spinlock
* @ack_rbf: ACK ts ring buffer
* @st_rbf: status ts ring buffer
*/
struct ath_dynack {
bool enabled;
int ackto;
unsigned long lto;
struct list_head nodes;
/* protect timestamp queue access */
spinlock_t qlock;
struct ath_dyn_rxbuf ack_rbf;
struct ath_dyn_txbuf st_rbf;
};
#if defined(CONFIG_ATH9K_DYNACK)
void ath_dynack_reset(struct ath_hw *ah);
void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an);
void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an);
void ath_dynack_init(struct ath_hw *ah);
void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, u32 ts);
void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
struct ath_tx_status *ts);
#else
static inline void ath_dynack_init(struct ath_hw *ah) {}
static inline void ath_dynack_node_init(struct ath_hw *ah,
struct ath_node *an) {}
static inline void ath_dynack_node_deinit(struct ath_hw *ah,
struct ath_node *an) {}
static inline void ath_dynack_sample_ack_ts(struct ath_hw *ah,
struct sk_buff *skb, u32 ts) {}
static inline void ath_dynack_sample_tx_ts(struct ath_hw *ah,
struct sk_buff *skb,
struct ath_tx_status *ts) {}
#endif
#endif /* DYNACK_H */
......@@ -1722,7 +1722,7 @@ static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
}
static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
u8 coverage_class)
s16 coverage_class)
{
struct ath9k_htc_priv *priv = hw->priv;
......
......@@ -647,6 +647,8 @@ int ath9k_hw_init(struct ath_hw *ah)
return ret;
}
ath_dynack_init(ah);
return 0;
}
EXPORT_SYMBOL(ath9k_hw_init);
......@@ -935,21 +937,21 @@ static void ath9k_hw_set_sifs_time(struct ath_hw *ah, u32 us)
REG_WRITE(ah, AR_D_GBL_IFS_SIFS, val);
}
static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
void ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
{
u32 val = ath9k_hw_mac_to_clks(ah, us);
val = min(val, (u32) 0xFFFF);
REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val);
}
static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us)
void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us)
{
u32 val = ath9k_hw_mac_to_clks(ah, us);
val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK));
REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val);
}
static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us)
void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us)
{
u32 val = ath9k_hw_mac_to_clks(ah, us);
val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS));
......@@ -1053,6 +1055,14 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
ctstimeout += 48 - sifstime - ah->slottime;
}
if (ah->dynack.enabled) {
acktimeout = ah->dynack.ackto;
ctstimeout = acktimeout;
slottime = (acktimeout - 3) / 2;
} else {
ah->dynack.ackto = acktimeout;
}
ath9k_hw_set_sifs_time(ah, sifstime);
ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, acktimeout);
......@@ -1954,6 +1964,12 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (AR_SREV_9565(ah) && common->bt_ant_diversity)
REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
if (ah->hw->conf.radar_enabled) {
/* set HW specific DFS configuration */
ah->radar_conf.ext_channel = IS_CHAN_HT40(chan);
ath9k_hw_set_radar_params(ah);
}
return 0;
}
EXPORT_SYMBOL(ath9k_hw_reset);
......
......@@ -29,6 +29,7 @@
#include "reg.h"
#include "phy.h"
#include "btcoex.h"
#include "dynack.h"
#include "../regd.h"
......@@ -924,6 +925,8 @@ struct ath_hw {
int (*external_reset)(void);
const struct firmware *eeprom_blob;
struct ath_dynack dynack;
};
struct ath_bus_ops {
......@@ -1080,6 +1083,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us);
void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us);
void ath9k_hw_setslottime(struct ath_hw *ah, u32 us);
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
{
......
......@@ -763,8 +763,9 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
hw->wiphy->features |= (NL80211_FEATURE_ACTIVE_MONITOR |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE);
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_P2P_GO_CTWIN;
if (!config_enabled(CONFIG_ATH9K_TX99)) {
hw->wiphy->interface_modes =
......@@ -810,7 +811,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
/* allow 4 queues per channel context +
* 1 cab queue + 1 offchannel tx queue
*/
hw->queues = 10;
hw->queues = ATH9K_NUM_TX_QUEUES;
/* last queue for offchannel */
hw->offchannel_tx_hw_queue = hw->queues - 1;
hw->max_rates = 4;
......
......@@ -121,6 +121,7 @@ struct ath_tx_status {
u32 evm0;
u32 evm1;
u32 evm2;
u32 duration[4];
};
struct ath_rx_status {
......
......@@ -224,16 +224,11 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
struct ath_common *common = ath9k_hw_common(ah);
unsigned long flags;
if (ath_startrecv(sc) != 0) {
ath_err(common, "Unable to restart recv logic\n");
return false;
}
ath9k_calculate_summary_state(sc, sc->cur_chan);
ath_startrecv(sc);
ath9k_cmn_update_txpow(ah, sc->curtxpow,
sc->cur_chan->txpower, &sc->curtxpow);
clear_bit(ATH_OP_HW_RESET, &common->op_flags);
ath9k_calculate_summary_state(sc, sc->cur_chan);
if (!sc->cur_chan->offchannel && start) {
/* restore per chanctx TSF timer */
......@@ -350,12 +345,16 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
memset(&an->key_idx, 0, sizeof(an->key_idx));
ath_tx_node_init(sc, an);
ath_dynack_node_init(sc->sc_ah, an);
}
static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
{
struct ath_node *an = (struct ath_node *)sta->drv_priv;
ath_tx_node_cleanup(sc, an);
ath_dynack_node_deinit(sc->sc_ah, an);
}
void ath9k_tasklet(unsigned long data)
......@@ -916,8 +915,6 @@ static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data,
switch (vif->type) {
case NL80211_IFTYPE_AP:
iter_data->naps++;
if (vif->bss_conf.enable_beacon)
iter_data->beacons = true;
break;
case NL80211_IFTYPE_STATION:
iter_data->nstations++;
......@@ -960,21 +957,6 @@ void ath9k_calculate_iter_data(struct ath_softc *sc,
list_for_each_entry(avp, &ctx->vifs, list)
ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif);
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
if (ctx == &sc->offchannel.chan) {
struct ieee80211_vif *vif;
if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
vif = sc->offchannel.scan_vif;
else
vif = sc->offchannel.roc_vif;
if (vif)
ath9k_vif_iter(iter_data, vif->addr, vif);
iter_data->beacons = false;
}
#endif
}
static void ath9k_set_assoc_state(struct ath_softc *sc,
......@@ -985,13 +967,6 @@ static void ath9k_set_assoc_state(struct ath_softc *sc,
unsigned long flags;
set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
/* Set the AID, BSSID and do beacon-sync only when
* the HW opmode is STATION.
*
* But the primary bit is set above in any case.
*/
if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
return;
ether_addr_copy(common->curbssid, bss_conf->bssid);
common->curaid = bss_conf->aid;
......@@ -1014,6 +989,43 @@ static void ath9k_set_assoc_state(struct ath_softc *sc,
vif->addr, common->curbssid);
}
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
static void ath9k_set_offchannel_state(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_vif *vif = NULL;
ath9k_ps_wakeup(sc);
if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
vif = sc->offchannel.scan_vif;
else
vif = sc->offchannel.roc_vif;
if (WARN_ON(!vif))
goto exit;
eth_zero_addr(common->curbssid);
eth_broadcast_addr(common->bssidmask);
ether_addr_copy(common->macaddr, vif->addr);
common->curaid = 0;
ah->opmode = vif->type;
ah->imask &= ~ATH9K_INT_SWBA;
ah->imask &= ~ATH9K_INT_TSFOOR;
ah->slottime = ATH9K_SLOT_TIME_9;
ath_hw_setbssidmask(common);
ath9k_hw_setopmode(ah);
ath9k_hw_write_associd(sc->sc_ah);
ath9k_hw_set_interrupts(ah);
ath9k_hw_init_global_settings(ah);
exit:
ath9k_ps_restore(sc);
}
#endif
/* Called with sc->mutex held. */
void ath9k_calculate_summary_state(struct ath_softc *sc,
struct ath_chanctx *ctx)
......@@ -1021,12 +1033,18 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_vif_iter_data iter_data;
struct ath_beacon_config *cur_conf;
ath_chanctx_check_active(sc, ctx);
if (ctx != sc->cur_chan)
return;
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
if (ctx == &sc->offchannel.chan)
return ath9k_set_offchannel_state(sc);
#endif
ath9k_ps_wakeup(sc);
ath9k_calculate_iter_data(sc, ctx, &iter_data);
......@@ -1037,8 +1055,11 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
ath_hw_setbssidmask(common);
if (iter_data.naps > 0) {
cur_conf = &ctx->beacon;
ath9k_hw_set_tsfadjust(ah, true);
ah->opmode = NL80211_IFTYPE_AP;
if (cur_conf->enable_beacon)
iter_data.beacons = true;
} else {
ath9k_hw_set_tsfadjust(ah, false);
......@@ -1067,13 +1088,11 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
if (ah->opmode == NL80211_IFTYPE_STATION) {
bool changed = (iter_data.primary_sta != ctx->primary_sta);
iter_data.beacons = true;
if (iter_data.primary_sta) {
iter_data.beacons = true;
ath9k_set_assoc_state(sc, iter_data.primary_sta,
changed);
if (!ctx->primary_sta ||
!ctx->primary_sta->bss_conf.assoc)
ctx->primary_sta = iter_data.primary_sta;
ctx->primary_sta = iter_data.primary_sta;
} else {
ctx->primary_sta = NULL;
memset(common->curbssid, 0, ETH_ALEN);
......@@ -1102,11 +1121,23 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
else
clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
ctx->primary_sta = iter_data.primary_sta;
ath9k_ps_restore(sc);
}
static void ath9k_assign_hw_queues(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
int i;
for (i = 0; i < IEEE80211_NUM_ACS; i++)
vif->hw_queue[i] = i;
if (vif->type == NL80211_IFTYPE_AP)
vif->cab_queue = hw->queues - 2;
else
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
......@@ -1115,12 +1146,11 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_node *an = &avp->mcast_node;
int i;
mutex_lock(&sc->mutex);
if (config_enabled(CONFIG_ATH9K_TX99)) {
if (sc->nvifs >= 1) {
if (sc->cur_chan->nvifs >= 1) {
mutex_unlock(&sc->mutex);
return -EOPNOTSUPP;
}
......@@ -1128,7 +1158,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
}
ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
sc->nvifs++;
sc->cur_chan->nvifs++;
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
......@@ -1138,12 +1168,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
avp->chanctx = sc->cur_chan;
list_add_tail(&avp->list, &avp->chanctx->vifs);
}
for (i = 0; i < IEEE80211_NUM_ACS; i++)
vif->hw_queue[i] = i;
if (vif->type == NL80211_IFTYPE_AP)
vif->cab_queue = hw->queues - 2;
else
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
ath9k_assign_hw_queues(hw, vif);
an->sc = sc;
an->sta = NULL;
......@@ -1163,7 +1189,6 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv;
int i;
mutex_lock(&sc->mutex);
......@@ -1183,14 +1208,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
for (i = 0; i < IEEE80211_NUM_ACS; i++)
vif->hw_queue[i] = i;
if (vif->type == NL80211_IFTYPE_AP)
vif->cab_queue = hw->queues - 2;
else
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
ath9k_assign_hw_queues(hw, vif);
ath9k_calculate_summary_state(sc, avp->chanctx);
mutex_unlock(&sc->mutex);
......@@ -1210,7 +1228,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
ath9k_p2p_remove_vif(sc, vif);
sc->nvifs--;
sc->cur_chan->nvifs--;
sc->tx99_vif = NULL;
if (!ath9k_is_chanctx_enabled())
list_del(&avp->list);
......@@ -1430,7 +1448,10 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw,
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
sc->rx.rxfilter = *total_flags;
spin_lock_bh(&sc->chan_lock);
sc->cur_chan->rxfilter = *total_flags;
spin_unlock_bh(&sc->chan_lock);
ath9k_ps_wakeup(sc);
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
......@@ -1695,9 +1716,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
(changed & BSS_CHANGED_BEACON_INT) ||
(changed & BSS_CHANGED_BEACON_INFO)) {
ath9k_beacon_config(sc, vif, changed);
if (changed & BSS_CHANGED_BEACON_ENABLED)
ath9k_calculate_summary_state(sc, avp->chanctx);
ath9k_beacon_config(sc, vif, changed);
}
if ((avp->chanctx == sc->cur_chan) &&
......@@ -1859,7 +1880,22 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
return 0;
}
static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
static void ath9k_enable_dynack(struct ath_softc *sc)
{
#ifdef CONFIG_ATH9K_DYNACK
u32 rfilt;
struct ath_hw *ah = sc->sc_ah;
ath_dynack_reset(ah);
ah->dynack.enabled = true;
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(ah, rfilt);
#endif
}
static void ath9k_set_coverage_class(struct ieee80211_hw *hw,
s16 coverage_class)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
......@@ -1868,11 +1904,22 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
return;
mutex_lock(&sc->mutex);
ah->coverage_class = coverage_class;
ath9k_ps_wakeup(sc);
ath9k_hw_init_global_settings(ah);
ath9k_ps_restore(sc);
if (coverage_class >= 0) {
ah->coverage_class = coverage_class;
if (ah->dynack.enabled) {
u32 rfilt;
ah->dynack.enabled = false;
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(ah, rfilt);
}
ath9k_ps_wakeup(sc);
ath9k_hw_init_global_settings(ah);
ath9k_ps_restore(sc);
} else if (!ah->dynack.enabled) {
ath9k_enable_dynack(sc);
}
mutex_unlock(&sc->mutex);
}
......
......@@ -387,7 +387,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
if (sc->hw->conf.radar_enabled)
rfilt |= ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR;
if (sc->rx.rxfilter & FIF_PROBE_REQ)
spin_lock_bh(&sc->chan_lock);
if (sc->cur_chan->rxfilter & FIF_PROBE_REQ)
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
/*
......@@ -398,24 +400,25 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
if (sc->sc_ah->is_monitoring)
rfilt |= ATH9K_RX_FILTER_PROM;
if (sc->rx.rxfilter & FIF_CONTROL)
if ((sc->cur_chan->rxfilter & FIF_CONTROL) ||
sc->sc_ah->dynack.enabled)
rfilt |= ATH9K_RX_FILTER_CONTROL;
if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
(sc->nvifs <= 1) &&
!(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC))
(sc->cur_chan->nvifs <= 1) &&
!(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC))
rfilt |= ATH9K_RX_FILTER_MYBEACON;
else
rfilt |= ATH9K_RX_FILTER_BEACON;
if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
(sc->rx.rxfilter & FIF_PSPOLL))
(sc->cur_chan->rxfilter & FIF_PSPOLL))
rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (conf_is_ht(&sc->hw->conf))
if (sc->cur_chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
rfilt |= ATH9K_RX_FILTER_COMP_BAR;
if (sc->nvifs > 1 || (sc->rx.rxfilter & FIF_OTHER_BSS)) {
if (sc->cur_chan->nvifs > 1 || (sc->cur_chan->rxfilter & FIF_OTHER_BSS)) {
/* This is needed for older chips */
if (sc->sc_ah->hw_version.macVersion <= AR_SREV_VERSION_9160)
rfilt |= ATH9K_RX_FILTER_PROM;
......@@ -429,18 +432,20 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
test_bit(ATH_OP_SCANNING, &common->op_flags))
rfilt |= ATH9K_RX_FILTER_BEACON;
spin_unlock_bh(&sc->chan_lock);
return rfilt;
}
int ath_startrecv(struct ath_softc *sc)
void ath_startrecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_rxbuf *bf, *tbf;
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_edma_start_recv(sc);
return 0;
return;
}
if (list_empty(&sc->rx.rxbuf))
......@@ -463,8 +468,6 @@ int ath_startrecv(struct ath_softc *sc)
start_recv:
ath_opmode_init(sc);
ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
return 0;
}
static void ath_flushrecv(struct ath_softc *sc)
......@@ -535,6 +538,7 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
bool skip_beacon = false;
if (skb->len < 24 + 8 + 2 + 2)
return;
......@@ -545,7 +549,16 @@ 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_chan->beacon.beacon_interval == 0)))
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
if (ath9k_is_chanctx_enabled()) {
if (sc->cur_chan == &sc->offchannel.chan)
skip_beacon = true;
}
#endif
if (!skip_beacon &&
!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
ath9k_set_beacon(sc);
ath9k_p2p_beacon_sync(sc);
......@@ -867,8 +880,13 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
* everything but the rate is checked here, the rate check is done
* separately to avoid doing two lookups for a rate for each frame.
*/
if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, sc->rx.rxfilter))
spin_lock_bh(&sc->chan_lock);
if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error,
sc->cur_chan->rxfilter)) {
spin_unlock_bh(&sc->chan_lock);
return -EINVAL;
}
spin_unlock_bh(&sc->chan_lock);
if (ath_is_mybeacon(common, hdr)) {
RX_STAT_INC(rx_beacons);
......@@ -894,7 +912,7 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
if (ath9k_is_chanctx_enabled()) {
if (rx_stats->is_mybeacon)
ath_chanctx_beacon_recv_ev(sc, rx_stats->rs_tstamp,
ath_chanctx_beacon_recv_ev(sc,
ATH_CHANCTX_EVENT_BEACON_RECEIVED);
}
......@@ -992,6 +1010,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
unsigned long flags;
dma_addr_t new_buf_addr;
unsigned int budget = 512;
struct ieee80211_hdr *hdr;
if (edma)
dma_type = DMA_BIDIRECTIONAL;
......@@ -1121,6 +1140,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
ath9k_apply_ampdu_details(sc, &rs, rxs);
ath_debug_rate_stats(sc, &rs, skb);
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_ack(hdr->frame_control))
ath_dynack_sample_ack_ts(sc->sc_ah, skb, rs.rs_tstamp);
ieee80211_rx(hw, skb);
requeue_drop_frag:
......
......@@ -174,7 +174,7 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf,
ssize_t len;
int r;
if (sc->nvifs > 1)
if (sc->cur_chan->nvifs > 1)
return -EOPNOTSUPP;
len = min(count, sizeof(buf) - 1);
......
......@@ -232,7 +232,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
goto fail_wow;
}
if (sc->nvifs > 1) {
if (sc->cur_chan->nvifs > 1) {
ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
ret = 1;
goto fail_wow;
......
......@@ -587,6 +587,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
memcpy(tx_info->control.rates, rates, sizeof(rates));
ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok);
rc_update = false;
if (bf == bf->bf_lastbf)
ath_dynack_sample_tx_ts(sc->sc_ah,
bf->bf_mpdu,
ts);
}
ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
......@@ -687,6 +691,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
memcpy(info->control.rates, bf->rates,
sizeof(info->control.rates));
ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts);
}
ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok);
} else
......
......@@ -39,3 +39,12 @@ config WIL6210_TRACING
option if you are interested in debugging the driver.
If unsure, say Y to make it easier to debug problems.
config WIL6210_PLATFORM_MSM
bool "wil6210 MSM platform specific support"
depends on WIL6210
depends on ARCH_MSM
default y
---help---
Say Y here to enable wil6210 driver support for MSM
platform specific features
......@@ -10,7 +10,10 @@ wil6210-y += interrupt.o
wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-y += rx_reorder.o
wil6210-y += fw.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
wil6210-y += wil_platform.o
wil6210-$(CONFIG_WIL6210_PLATFORM_MSM) += wil_platform_msm.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
......
......@@ -296,6 +296,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
n = min(request->n_channels, 4U);
for (i = 0; i < n; i++) {
int ch = request->channels[i]->hw_value;
if (ch == 0) {
wil_err(wil,
"Scan requested for unknown frequency %dMhz\n",
......@@ -308,9 +309,23 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
request->channels[i]->center_freq);
}
if (request->ie_len)
print_hex_dump_bytes("Scan IE ", DUMP_PREFIX_OFFSET,
request->ie, request->ie_len);
else
wil_dbg_misc(wil, "Scan has no IE's\n");
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len,
request->ie);
if (rc) {
wil_err(wil, "Aborting scan, set_ie failed: %d\n", rc);
goto out;
}
rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
out:
if (rc) {
del_timer_sync(&wil->scan_timer);
wil->scan_request = NULL;
......@@ -319,6 +334,22 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return rc;
}
static void wil_print_connect_params(struct wil6210_priv *wil,
struct cfg80211_connect_params *sme)
{
wil_info(wil, "Connecting to:\n");
if (sme->channel) {
wil_info(wil, " Channel: %d freq %d\n",
sme->channel->hw_value, sme->channel->center_freq);
}
if (sme->bssid)
wil_info(wil, " BSSID: %pM\n", sme->bssid);
if (sme->ssid)
print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
16, 1, sme->ssid, sme->ssid_len, true);
wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *sme)
......@@ -335,6 +366,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
test_bit(wil_status_fwconnected, &wil->status))
return -EALREADY;
wil_print_connect_params(wil, sme);
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
......@@ -360,22 +393,22 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
sme->ie_len);
goto out;
}
/*
* For secure assoc, send:
* (1) WMI_DELETE_CIPHER_KEY_CMD
* (2) WMI_SET_APPIE_CMD
*/
/* For secure assoc, send WMI_DELETE_CIPHER_KEY_CMD */
rc = wmi_del_cipher_key(wil, 0, bss->bssid);
if (rc) {
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n");
goto out;
}
/* WMI_SET_APPIE_CMD */
rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
if (rc) {
wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
goto out;
}
}
/* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info
* elements. Send it also in case it's empty, to erase previously set
* ies in FW.
*/
rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
if (rc) {
wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
goto out;
}
/* WMI_CONNECT_CMD */
......@@ -621,6 +654,45 @@ static int wil_fix_bcon(struct wil6210_priv *wil,
return rc;
}
static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_beacon_data *bcon)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
if (wil_fix_bcon(wil, bcon)) {
wil_dbg_misc(wil, "Fixed bcon\n");
wil_print_bcon_data(bcon);
}
/* FW do not form regular beacon, so bcon IE's are not set
* For the DMG bcon, when it will be supported, bcon IE's will
* be reused; add something like:
* wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
* bcon->beacon_ies);
*/
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP,
bcon->proberesp_ies_len,
bcon->proberesp_ies);
if (rc) {
wil_err(wil, "set_ie(PROBE_RESP) failed\n");
return rc;
}
rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
bcon->assocresp_ies_len,
bcon->assocresp_ies);
if (rc) {
wil_err(wil, "set_ie(ASSOC_RESP) failed\n");
return rc;
}
return 0;
}
static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_ap_settings *info)
......@@ -658,12 +730,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
mutex_lock(&wil->mutex);
rc = wil_reset(wil);
if (rc)
goto out;
/* Rx VRING. */
rc = wil_rx_init(wil);
__wil_down(wil);
rc = __wil_up(wil);
if (rc)
goto out;
......@@ -671,9 +739,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (rc)
goto out;
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
/* IE's */
/* bcon 'head IE's are not relevant for 60g band */
/*
......@@ -695,7 +760,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (rc)
goto out;
netif_carrier_on(ndev);
out:
......@@ -706,7 +770,7 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *ndev)
{
int rc = 0;
int rc, rc1;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s()\n", __func__);
......@@ -715,8 +779,12 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
rc = wmi_pcp_stop(wil);
__wil_down(wil);
rc1 = __wil_up(wil);
mutex_unlock(&wil->mutex);
return rc;
return min(rc, rc1);
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
......@@ -746,6 +814,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
.del_key = wil_cfg80211_del_key,
.set_default_key = wil_cfg80211_set_default_key,
/* AP mode */
.change_beacon = wil_cfg80211_change_beacon,
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
.del_station = wil_cfg80211_del_station,
......@@ -755,6 +824,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
{
/* TODO: set real value */
wiphy->max_scan_ssids = 10;
wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
wiphy->max_num_pmkids = 0 /* TODO: */;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
......@@ -764,8 +834,8 @@ static void wil_wiphy_init(struct wiphy *wiphy)
*/
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
__func__, wiphy->flags);
dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
__func__, wiphy->flags);
wiphy->probe_resp_offload =
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
......@@ -786,7 +856,9 @@ struct wireless_dev *wil_cfg80211_init(struct device *dev)
int rc = 0;
struct wireless_dev *wdev;
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
dev_dbg(dev, "%s()\n", __func__);
wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
if (!wdev)
return ERR_PTR(-ENOMEM);
......@@ -818,6 +890,8 @@ void wil_wdev_free(struct wil6210_priv *wil)
{
struct wireless_dev *wdev = wil_to_wdev(wil);
dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
if (!wdev)
return;
......
......@@ -17,43 +17,37 @@
#include "wil6210.h"
#include "trace.h"
int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
void wil_err(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_err(ndev, "%pV", &vaf);
netdev_err(ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
return ret;
}
int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_info(ndev, "%pV", &vaf);
netdev_info(ndev, "%pV", &vaf);
trace_wil6210_log_info(&vaf);
va_end(args);
return ret;
}
int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
......@@ -64,6 +58,4 @@ int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
vaf.va = &args;
trace_wil6210_log_dbg(&vaf);
va_end(args);
return 0;
}
......@@ -61,20 +61,22 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
if (x)
seq_printf(s, "0x%08x\n", ioread32(x));
else
seq_printf(s, "???\n");
seq_puts(s, "???\n");
if (vring->va && (vring->size < 1025)) {
uint i;
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &vring->va[i].tx;
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
seq_puts(s, "\n");
seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
_s : (vring->ctx[i].skb ? _h : 'h'));
}
seq_printf(s, "\n");
seq_puts(s, "\n");
}
seq_printf(s, "}\n");
seq_puts(s, "}\n");
}
static int wil_vring_debugfs_show(struct seq_file *s, void *data)
......@@ -85,7 +87,7 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_');
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
struct vring *vring = &wil->vring_tx[i];
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
if (vring->va) {
......@@ -163,7 +165,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
if (!wmi_addr(wil, r.base) ||
!wmi_addr(wil, r.tail) ||
!wmi_addr(wil, r.head)) {
seq_printf(s, " ??? pointers are garbage?\n");
seq_puts(s, " ??? pointers are garbage?\n");
goto out;
}
......@@ -182,6 +184,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
le32_to_cpu(d.addr));
if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
u16 len = le16_to_cpu(hdr.len);
seq_printf(s, " -> %04x %04x %04x %02x\n",
le16_to_cpu(hdr.seq), len,
le16_to_cpu(hdr.type), hdr.flags);
......@@ -199,6 +202,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
wil_memcpy_fromio_32(databuf, src, len);
while (n < len) {
int l = min(len - n, 16);
hex_dump_to_buffer(databuf + n, l,
16, 1, printbuf,
sizeof(printbuf),
......@@ -208,11 +212,11 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
}
}
} else {
seq_printf(s, "\n");
seq_puts(s, "\n");
}
}
out:
seq_printf(s, "}\n");
seq_puts(s, "}\n");
}
static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
......@@ -271,11 +275,13 @@ 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");
......@@ -302,7 +308,7 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
int i;
for (i = 0; tbl[i].name; i++) {
struct dentry *f = NULL;
struct dentry *f;
switch (tbl[i].type) {
case doff_u32:
......@@ -322,6 +328,8 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
tbl[i].mode, dbg,
base + tbl[i].off);
break;
default:
f = ERR_PTR(-EINVAL);
}
if (IS_ERR_OR_NULL(f))
wil_err(wil, "Create file \"%s\": err %ld\n",
......@@ -339,6 +347,7 @@ static const struct dbg_off isr_off[] = {
{"IMC", S_IWUSR, offsetof(struct RGF_ICR, IMC), doff_io32},
{},
};
static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
const char *name,
struct dentry *parent, u32 off)
......@@ -422,7 +431,7 @@ static const struct file_operations fops_memread = {
};
static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
{
enum { max_count = 4096 };
struct debugfs_blob_wrapper *blob = file->private_data;
......@@ -474,6 +483,7 @@ struct dentry *wil_debugfs_create_ioblob(const char *name,
{
return debugfs_create_file(name, mode, parent, blob, &fops_ioblob);
}
/*---reset---*/
static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
......@@ -499,6 +509,7 @@ 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)
......@@ -509,6 +520,7 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
bool on;
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
if (copy_from_user(kbuf, buf, len)) {
......@@ -545,6 +557,7 @@ 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,
......@@ -555,8 +568,8 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
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;
......@@ -625,8 +638,10 @@ static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
{
char printbuf[16 * 3 + 2];
int i = 0;
while (i < len) {
int l = min(len - i, 16);
hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
sizeof(printbuf), false);
seq_printf(s, "%s%s\n", prefix, printbuf);
......@@ -664,10 +679,8 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
struct wil6210_priv *wil = s->private;
struct vring *vring;
bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS);
if (tx)
vring = &(wil->vring_tx[dbg_vring_index]);
else
vring = &wil->vring_rx;
vring = tx ? &wil->vring_tx[dbg_vring_index] : &wil->vring_rx;
if (!vring->va) {
if (tx)
......@@ -682,7 +695,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
* only field used, .dma.length, is the same
*/
volatile struct vring_tx_desc *d =
&(vring->va[dbg_txdesc_index].tx);
&vring->va[dbg_txdesc_index].tx;
volatile u32 *u = (volatile u32 *)d;
struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
......@@ -702,7 +715,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
wil_seq_print_skb(s, skb);
kfree_skb(skb);
}
seq_printf(s, "}\n");
seq_puts(s, "}\n");
} else {
if (tx)
seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
......@@ -816,6 +829,7 @@ static const struct file_operations fops_bf = {
.read = seq_read,
.llseek = seq_lseek,
};
/*---------SSID------------*/
static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
......@@ -878,10 +892,10 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
u32 t_m, t_r;
int rc = wmi_get_temperature(wil, &t_m, &t_r);
if (rc) {
seq_printf(s, "Failed\n");
seq_puts(s, "Failed\n");
return 0;
}
......@@ -937,6 +951,7 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
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 ";
......@@ -997,7 +1012,6 @@ static int wil_info_debugfs_show(struct seq_file *s, void *data)
rxf_old = rxf;
txf_old = txf;
#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
" " __stringify(x) : ""
......@@ -1032,6 +1046,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
{
int i;
u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
seq_printf(s, "0x%03x [", r->head_seq_num);
for (i = 0; i < r->buf_size; i++) {
if (i == index)
......@@ -1046,10 +1061,12 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
int i, tid;
unsigned long flags;
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 ";
......@@ -1065,13 +1082,16 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data)
(p->data_port_open ? " data_port_open" : ""));
if (p->status == wil_sta_connected) {
spin_lock_irqsave(&p->tid_rx_lock, flags);
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
if (r) {
seq_printf(s, "[%2d] ", tid);
wil_print_rxtid(s, r);
}
}
spin_unlock_irqrestore(&p->tid_rx_lock, flags);
}
}
......
/*
* 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 <linux/firmware.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/crc32.h>
#include "wil6210.h"
#include "fw.h"
MODULE_FIRMWARE(WIL_FW_NAME);
/* target operations */
/* register read */
#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write. wmb() to make sure it is completed */
#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0)
/* register set = read, OR, write */
#define S(a, v) W(a, R(a) | v)
/* register clear = read, AND with inverted, write */
#define C(a, v) W(a, R(a) & ~v)
static
void wil_memset_toio_32(volatile void __iomem *dst, u32 val,
size_t count)
{
volatile u32 __iomem *d = dst;
for (count += 4; count > 4; count -= 4)
__raw_writel(val, d++);
}
#include "fw_inc.c"
/*
* 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.
*/
#define WIL_FW_SIGNATURE (0x36323130) /* '0126' */
#define WIL_FW_FMT_VERSION (1) /* format version driver supports */
enum wil_fw_record_type {
wil_fw_type_comment = 1,
wil_fw_type_data = 2,
wil_fw_type_fill = 3,
wil_fw_type_action = 4,
wil_fw_type_verify = 5,
wil_fw_type_file_header = 6,
wil_fw_type_direct_write = 7,
wil_fw_type_gateway_data = 8,
wil_fw_type_gateway_data4 = 9,
};
struct wil_fw_record_head {
__le16 type; /* enum wil_fw_record_type */
__le16 flags; /* to be defined */
__le32 size; /* whole record, bytes after head */
} __packed;
/* data block. write starting from @addr
* data_size inferred from the @head.size. For this case,
* data_size = @head.size - offsetof(struct wil_fw_record_data, data)
*/
struct wil_fw_record_data { /* type == wil_fw_type_data */
__le32 addr;
__le32 data[0]; /* [data_size], see above */
} __packed;
/* fill with constant @value, @size bytes starting from @addr */
struct wil_fw_record_fill { /* type == wil_fw_type_fill */
__le32 addr;
__le32 value;
__le32 size;
} __packed;
/* free-form comment
* for informational purpose, data_size is @head.size from record header
*/
struct wil_fw_record_comment { /* type == wil_fw_type_comment */
u8 data[0]; /* free-form data [data_size], see above */
} __packed;
/* perform action
* data_size = @head.size - offsetof(struct wil_fw_record_action, data)
*/
struct wil_fw_record_action { /* type == wil_fw_type_action */
__le32 action; /* action to perform: reset, wait for fw ready etc. */
__le32 data[0]; /* action specific, [data_size], see above */
} __packed;
/* data block for struct wil_fw_record_direct_write */
struct wil_fw_data_dwrite {
__le32 addr;
__le32 value;
__le32 mask;
} __packed;
/* write @value to the @addr,
* preserve original bits accordingly to the @mask
* data_size is @head.size where @head is record header
*/
struct wil_fw_record_direct_write { /* type == wil_fw_type_direct_write */
struct wil_fw_data_dwrite data[0];
} __packed;
/* verify condition: [@addr] & @mask == @value
* if condition not met, firmware download fails
*/
struct wil_fw_record_verify { /* type == wil_fw_verify */
__le32 addr; /* read from this address */
__le32 value; /* reference value */
__le32 mask; /* mask for verification */
} __packed;
/* file header
* First record of every file
*/
struct wil_fw_record_file_header {
__le32 signature ; /* Wilocity signature */
__le32 reserved;
__le32 crc; /* crc32 of the following data */
__le32 version; /* format version */
__le32 data_len; /* total data in file, including this record */
u8 comment[32]; /* short description */
} __packed;
/* 1-dword gateway */
/* data block for the struct wil_fw_record_gateway_data */
struct wil_fw_data_gw {
__le32 addr;
__le32 value;
} __packed;
/* gateway write block.
* write starting address and values from the data buffer
* through the gateway
* data_size inferred from the @head.size. For this case,
* data_size = @head.size - offsetof(struct wil_fw_record_gateway_data, data)
*/
struct wil_fw_record_gateway_data { /* type == wil_fw_type_gateway_data */
__le32 gateway_addr_addr;
__le32 gateway_value_addr;
__le32 gateway_cmd_addr;
__le32 gateway_ctrl_address;
#define WIL_FW_GW_CTL_BUSY BIT(29) /* gateway busy performing operation */
#define WIL_FW_GW_CTL_RUN BIT(30) /* start gateway operation */
__le32 command;
struct wil_fw_data_gw data[0]; /* total size [data_size], see above */
} __packed;
/* 4-dword gateway */
/* data block for the struct wil_fw_record_gateway_data4 */
struct wil_fw_data_gw4 {
__le32 addr;
__le32 value[4];
} __packed;
/* gateway write block.
* write starting address and values from the data buffer
* through the gateway
* data_size inferred from the @head.size. For this case,
* data_size = @head.size - offsetof(struct wil_fw_record_gateway_data4, data)
*/
struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */
__le32 gateway_addr_addr;
__le32 gateway_value_addr[4];
__le32 gateway_cmd_addr;
__le32 gateway_ctrl_address; /* same logic as for 1-dword gw */
__le32 command;
struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */
} __packed;
/*
* 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.
*/
/* Algorithmic part of the firmware download.
* To be included in the container file providing framework
*/
#define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)
#define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)
#define wil_hex_dump_fw(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
print_hex_dump_debug("DBG[ FW ]" prefix_str, \
prefix_type, rowsize, \
groupsize, buf, len, ascii)
#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
ioaddr = wmi_buffer(wil, val); \
if (!ioaddr) { \
wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
le32_to_cpu(val)); \
return -EINVAL; \
} \
} while (0)
/**
* wil_fw_verify - verify firmware file validity
*
* perform various checks for the firmware file header.
* records are not validated.
*
* Return file size or negative error
*/
static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size)
{
const struct wil_fw_record_head *hdr = (const void *)data;
struct wil_fw_record_file_header fh;
const struct wil_fw_record_file_header *fh_;
u32 crc;
u32 dlen;
if (size % 4) {
wil_err_fw(wil, "image size not aligned: %zu\n", size);
return -EINVAL;
}
/* have enough data for the file header? */
if (size < sizeof(*hdr) + sizeof(fh)) {
wil_err_fw(wil, "file too short: %zu bytes\n", size);
return -EINVAL;
}
/* start with the file header? */
if (le16_to_cpu(hdr->type) != wil_fw_type_file_header) {
wil_err_fw(wil, "no file header\n");
return -EINVAL;
}
/* data_len */
fh_ = (struct wil_fw_record_file_header *)&hdr[1];
dlen = le32_to_cpu(fh_->data_len);
if (dlen % 4) {
wil_err_fw(wil, "data length not aligned: %lu\n", (ulong)dlen);
return -EINVAL;
}
if (size < dlen) {
wil_err_fw(wil, "file truncated at %zu/%lu\n",
size, (ulong)dlen);
return -EINVAL;
}
if (dlen < sizeof(*hdr) + sizeof(fh)) {
wil_err_fw(wil, "data length too short: %lu\n", (ulong)dlen);
return -EINVAL;
}
/* signature */
if (le32_to_cpu(fh_->signature) != WIL_FW_SIGNATURE) {
wil_err_fw(wil, "bad header signature: 0x%08x\n",
le32_to_cpu(fh_->signature));
return -EINVAL;
}
/* version */
if (le32_to_cpu(fh_->version) > WIL_FW_FMT_VERSION) {
wil_err_fw(wil, "unsupported header version: %d\n",
le32_to_cpu(fh_->version));
return -EINVAL;
}
/* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/
fh = *fh_;
fh.crc = 0;
crc = crc32_le(~0, (unsigned char const *)hdr, sizeof(*hdr));
crc = crc32_le(crc, (unsigned char const *)&fh, sizeof(fh));
crc = crc32_le(crc, (unsigned char const *)&fh_[1],
dlen - sizeof(*hdr) - sizeof(fh));
crc = ~crc;
if (crc != le32_to_cpu(fh_->crc)) {
wil_err_fw(wil, "checksum mismatch:"
" calculated for %lu bytes 0x%08x != 0x%08x\n",
(ulong)dlen, crc, le32_to_cpu(fh_->crc));
return -EINVAL;
}
return (int)dlen;
}
static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
return 0;
}
static int fw_handle_data(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_data *d = data;
void __iomem *dst;
size_t s = size - sizeof(*d);
if (size < sizeof(*d) + sizeof(u32)) {
wil_err_fw(wil, "data record too short: %zu\n", size);
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s);
wil_memcpy_toio_32(dst, d->data, s);
wmb(); /* finish before processing next record */
return 0;
}
static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_fill *d = data;
void __iomem *dst;
u32 v;
size_t s = (size_t)le32_to_cpu(d->size);
if (size != sizeof(*d)) {
wil_err_fw(wil, "bad size for fill record: %zu\n", size);
return -EINVAL;
}
if (s < sizeof(u32)) {
wil_err_fw(wil, "fill size too short: %zu\n", s);
return -EINVAL;
}
if (s % sizeof(u32)) {
wil_err_fw(wil, "fill size not aligned: %zu\n", s);
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
v = le32_to_cpu(d->value);
wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
le32_to_cpu(d->addr), v, s);
wil_memset_toio_32(dst, v, s);
wmb(); /* finish before processing next record */
return 0;
}
static int fw_handle_file_header(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_file_header *d = data;
if (size != sizeof(*d)) {
wil_err_fw(wil, "file header length incorrect: %zu\n", size);
return -EINVAL;
}
wil_dbg_fw(wil, "new file, ver. %d, %i bytes\n",
d->version, d->data_len);
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment,
sizeof(d->comment), true);
return 0;
}
static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_direct_write *d = data;
const struct wil_fw_data_dwrite *block = d->data;
int n, i;
if (size % sizeof(*block)) {
wil_err_fw(wil, "record size not aligned on %zu: %zu\n",
sizeof(*block), size);
return -EINVAL;
}
n = size / sizeof(*block);
for (i = 0; i < n; i++) {
void __iomem *dst;
u32 m = le32_to_cpu(block[i].mask);
u32 v = le32_to_cpu(block[i].value);
u32 x, y;
FW_ADDR_CHECK(dst, block[i].addr, "address");
x = ioread32(dst);
y = (x & m) | (v & ~m);
wil_dbg_fw(wil, "write [0x%08x] <== 0x%08x "
"(old 0x%08x val 0x%08x mask 0x%08x)\n",
le32_to_cpu(block[i].addr), y, x, v, m);
iowrite32(y, dst);
wmb(); /* finish before processing next record */
}
return 0;
}
static int gw_write(struct wil6210_priv *wil, void __iomem *gwa_addr,
void __iomem *gwa_cmd, void __iomem *gwa_ctl, u32 gw_cmd,
u32 a)
{
unsigned delay = 0;
iowrite32(a, gwa_addr);
iowrite32(gw_cmd, gwa_cmd);
wmb(); /* finish before activate gw */
iowrite32(WIL_FW_GW_CTL_RUN, gwa_ctl); /* activate gw */
do {
udelay(1); /* typical time is few usec */
if (delay++ > 100) {
wil_err_fw(wil, "gw timeout\n");
return -EINVAL;
}
} while (ioread32(gwa_ctl) & WIL_FW_GW_CTL_BUSY); /* gw done? */
return 0;
}
static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_gateway_data *d = data;
const struct wil_fw_data_gw *block = d->data;
void __iomem *gwa_addr;
void __iomem *gwa_val;
void __iomem *gwa_cmd;
void __iomem *gwa_ctl;
u32 gw_cmd;
int n, i;
if (size < sizeof(*d) + sizeof(*block)) {
wil_err_fw(wil, "gateway record too short: %zu\n", size);
return -EINVAL;
}
if ((size - sizeof(*d)) % sizeof(*block)) {
wil_err_fw(wil, "gateway record data size"
" not aligned on %zu: %zu\n",
sizeof(*block), size - sizeof(*d));
return -EINVAL;
}
n = (size - sizeof(*d)) / sizeof(*block);
gw_cmd = le32_to_cpu(d->command);
wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
" cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr),
le32_to_cpu(d->gateway_value_addr),
le32_to_cpu(d->gateway_cmd_addr),
le32_to_cpu(d->gateway_ctrl_address));
for (i = 0; i < n; i++) {
int rc;
u32 a = le32_to_cpu(block[i].addr);
u32 v = le32_to_cpu(block[i].value);
wil_dbg_fw(wil, " gw write[%3d] [0x%08x] <== 0x%08x\n",
i, a, v);
iowrite32(v, gwa_val);
rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
if (rc)
return rc;
}
return 0;
}
static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_gateway_data4 *d = data;
const struct wil_fw_data_gw4 *block = d->data;
void __iomem *gwa_addr;
void __iomem *gwa_val[ARRAY_SIZE(block->value)];
void __iomem *gwa_cmd;
void __iomem *gwa_ctl;
u32 gw_cmd;
int n, i, k;
if (size < sizeof(*d) + sizeof(*block)) {
wil_err_fw(wil, "gateway4 record too short: %zu\n", size);
return -EINVAL;
}
if ((size - sizeof(*d)) % sizeof(*block)) {
wil_err_fw(wil, "gateway4 record data size"
" not aligned on %zu: %zu\n",
sizeof(*block), size - sizeof(*d));
return -EINVAL;
}
n = (size - sizeof(*d)) / sizeof(*block);
gw_cmd = le32_to_cpu(d->command);
wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
for (k = 0; k < ARRAY_SIZE(block->value); k++)
FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
"gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr),
le32_to_cpu(d->gateway_cmd_addr),
le32_to_cpu(d->gateway_ctrl_address));
wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE, 16, 4,
d->gateway_value_addr, sizeof(d->gateway_value_addr),
false);
for (i = 0; i < n; i++) {
int rc;
u32 a = le32_to_cpu(block[i].addr);
u32 v[ARRAY_SIZE(block->value)];
for (k = 0; k < ARRAY_SIZE(block->value); k++)
v[k] = le32_to_cpu(block[i].value[k]);
wil_dbg_fw(wil, " gw4 write[%3d] [0x%08x] <==\n", i, a);
wil_hex_dump_fw(" val ", DUMP_PREFIX_NONE, 16, 4, v,
sizeof(v), false);
for (k = 0; k < ARRAY_SIZE(block->value); k++)
iowrite32(v[k], gwa_val[k]);
rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
if (rc)
return rc;
}
return 0;
}
static const struct {
int type;
int (*handler)(struct wil6210_priv *wil, const void *data, size_t size);
} wil_fw_handlers[] = {
{wil_fw_type_comment, fw_handle_comment},
{wil_fw_type_data, fw_handle_data},
{wil_fw_type_fill, fw_handle_fill},
/* wil_fw_type_action */
/* wil_fw_type_verify */
{wil_fw_type_file_header, fw_handle_file_header},
{wil_fw_type_direct_write, fw_handle_direct_write},
{wil_fw_type_gateway_data, fw_handle_gateway_data},
{wil_fw_type_gateway_data4, fw_handle_gateway_data4},
};
static int wil_fw_handle_record(struct wil6210_priv *wil, int type,
const void *data, size_t size)
{
int i;
for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++) {
if (wil_fw_handlers[i].type == type)
return wil_fw_handlers[i].handler(wil, data, size);
}
wil_err_fw(wil, "unknown record type: %d\n", type);
return -EINVAL;
}
/**
* wil_fw_load - load FW into device
*
* Load the FW and uCode code and data to the corresponding device
* memory regions
*
* Return error code
*/
static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
{
int rc = 0;
const struct wil_fw_record_head *hdr;
size_t s, hdr_sz;
for (hdr = data;; hdr = (const void *)hdr + s, size -= s) {
if (size < sizeof(*hdr))
break;
hdr_sz = le32_to_cpu(hdr->size);
s = sizeof(*hdr) + hdr_sz;
if (s > size)
break;
if (hdr_sz % 4) {
wil_err_fw(wil, "unaligned record size: %zu\n",
hdr_sz);
return -EINVAL;
}
rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
&hdr[1], hdr_sz);
if (rc)
return rc;
}
if (size) {
wil_err_fw(wil, "unprocessed bytes: %zu\n", size);
if (size >= sizeof(*hdr)) {
wil_err_fw(wil, "Stop at offset %ld"
" record type %d [%zd bytes]\n",
(const void *)hdr - data,
le16_to_cpu(hdr->type), hdr_sz);
}
return -EINVAL;
}
/* Mark FW as loaded from host */
S(RGF_USER_USAGE_6, 1);
return rc;
}
/**
* wil_request_firmware - Request firmware and load to device
*
* Request firmware image from the file and load it to device
*
* Return error code
*/
int wil_request_firmware(struct wil6210_priv *wil, const char *name)
{
int rc, rc1;
const struct firmware *fw;
size_t sz;
const void *d;
rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load firmware %s\n", name);
return rc;
}
wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) {
rc1 = wil_fw_verify(wil, d, sz);
if (rc1 < 0) {
rc = rc1;
goto out;
}
rc = wil_fw_load(wil, d, rc1);
if (rc < 0)
goto out;
}
out:
release_firmware(fw);
return rc;
}
......@@ -135,7 +135,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
}
void wil6210_disable_irq(struct wil6210_priv *wil)
void wil_mask_irq(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
......@@ -145,7 +145,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil)
wil6210_mask_irq_pseudo(wil);
}
void wil6210_enable_irq(struct wil6210_priv *wil)
void wil_unmask_irq(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
......@@ -196,8 +196,13 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
if (test_bit(wil_status_reset_done, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
napi_schedule(&wil->napi_rx);
if (test_bit(wil_status_napi_en, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
napi_schedule(&wil->napi_rx);
} else {
wil_err(wil, "Got Rx interrupt while "
"stopping interface\n");
}
} else {
wil_err(wil, "Got Rx interrupt while in reset\n");
}
......@@ -506,7 +511,8 @@ static int wil6210_request_3msi(struct wil6210_priv *wil, int irq)
return rc;
}
/* can't use wil_ioread32_and_clear because ICC value is not ser yet */
/* can't use wil_ioread32_and_clear because ICC value is not set yet */
static inline void wil_clear32(void __iomem *addr)
{
u32 x = ioread32(addr);
......@@ -522,11 +528,15 @@ void wil6210_clear_irq(struct wil6210_priv *wil)
offsetof(struct RGF_ICR, ICR));
wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
wmb(); /* make sure write completed */
}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
int rc;
wil_dbg_misc(wil, "%s() n_msi=%d\n", __func__, wil->n_msi);
if (wil->n_msi == 3)
rc = wil6210_request_3msi(wil, irq);
else
......@@ -534,17 +544,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq)
wil6210_thread_irq,
wil->n_msi ? 0 : IRQF_SHARED,
WIL_NAME, wil);
if (rc)
return rc;
wil6210_enable_irq(wil);
return 0;
return rc;
}
void wil6210_fini_irq(struct wil6210_priv *wil, int irq)
{
wil6210_disable_irq(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
wil_mask_irq(wil);
free_irq(irq, wil);
if (wil->n_msi == 3) {
free_irq(irq + 1, wil);
......
......@@ -20,11 +20,19 @@
#include "wil6210.h"
#include "txrx.h"
#include "wmi.h"
#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
static bool no_fw_recovery;
module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
static bool no_fw_load = true;
module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(no_fw_load, " do not download FW, use one in on-card flash.");
#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
......@@ -67,6 +75,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
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);
......@@ -86,9 +95,16 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
}
for (i = 0; i < WIL_STA_TID_NUM; i++) {
struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
struct wil_tid_ampdu_rx *r;
unsigned long flags;
spin_lock_irqsave(&sta->tid_rx_lock, flags);
r = sta->tid_rx[i];
sta->tid_rx[i] = NULL;
wil_tid_ampdu_rx_free(wil, r);
spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
}
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
if (wil->vring2cid_tid[i][0] == cid)
......@@ -205,10 +221,8 @@ static void wil_fw_error_worker(struct work_struct *work)
case NL80211_IFTYPE_MONITOR:
wil_info(wil, "fw error recovery started (try %d)...\n",
wil->recovery_count);
wil_reset(wil);
/* need to re-allocate Rx ring after reset */
wil_rx_init(wil);
__wil_down(wil);
__wil_up(wil);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
......@@ -223,6 +237,7 @@ static void wil_fw_error_worker(struct work_struct *work)
static int wil_find_free_vring(struct wil6210_priv *wil)
{
int i;
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
if (!wil->vring_tx[i].va)
return i;
......@@ -257,14 +272,19 @@ static void wil_connect_worker(struct work_struct *work)
int wil_priv_init(struct wil6210_priv *wil)
{
uint i;
wil_dbg_misc(wil, "%s()\n", __func__);
memset(wil->sta, 0, sizeof(wil->sta));
for (i = 0; i < WIL6210_MAX_CID; i++)
spin_lock_init(&wil->sta[i].tid_rx_lock);
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
init_completion(&wil->wmi_ready);
init_completion(&wil->wmi_call);
wil->pending_connect_cid = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
......@@ -295,12 +315,16 @@ int wil_priv_init(struct wil6210_priv *wil)
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
{
wil_dbg_misc(wil, "%s()\n", __func__);
del_timer_sync(&wil->connect_timer);
_wil6210_disconnect(wil, bssid);
}
void wil_priv_deinit(struct wil6210_priv *wil)
{
wil_dbg_misc(wil, "%s()\n", __func__);
del_timer_sync(&wil->scan_timer);
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
......@@ -312,6 +336,28 @@ void wil_priv_deinit(struct wil6210_priv *wil)
destroy_workqueue(wil->wmi_wq);
}
/* target operations */
/* register read */
#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write. wmb() to make sure it is completed */
#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0)
/* register set = read, OR, write */
#define S(a, v) W(a, R(a) | v)
/* register clear = read, AND with inverted, write */
#define C(a, v) W(a, R(a) & ~v)
static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
W(RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
W(RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST);
}
static inline void wil_release_cpu(struct wil6210_priv *wil)
{
/* Start CPU */
W(RGF_USER_USER_CPU_0, 1);
}
static int wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
......@@ -321,60 +367,41 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name);
/* register read */
#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
#define S(a, v) W(a, R(a) | v)
/* register clear = read, AND with inverted, write */
#define C(a, v) W(a, R(a) & ~v)
wmb(); /* If host reorder writes here -> race in NIC */
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
wil->hw_version = R(RGF_USER_FW_REV_ID);
rev_id = wil->hw_version & 0xff;
/* Clear MAC link up */
S(RGF_HP_CTRL, BIT(15));
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
wmb(); /* order is important here */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
wil_halt_cpu(wil);
C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); /* 40 MHz */
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
}
W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
wmb(); /* If host reorder writes here -> race in NIC */
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000B0 : 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000f0 : 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
}
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
wmb(); /* order is important here */
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
/* reset A2 PCIE AHB */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
} else {
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
if (rev_id == 1) {
......@@ -384,12 +411,10 @@ static int wil_target_reset(struct wil6210_priv *wil)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
}
}
/* TODO: check order here!!! Erez code is different */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
wmb(); /* order is important here */
/* wait until device ready. typical time is 200..250 msec */
do {
......@@ -407,16 +432,15 @@ static int wil_target_reset(struct wil6210_priv *wil)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
wmb(); /* order is important here */
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
return 0;
}
#undef R
#undef W
#undef S
#undef C
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
......@@ -431,6 +455,7 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
ulong to = msecs_to_jiffies(1000);
ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
if (0 == left) {
wil_err(wil, "Firmware not ready\n");
return -ETIME;
......@@ -450,15 +475,15 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
WARN_ON(!mutex_is_locked(&wil->mutex));
WARN_ON(test_bit(wil_status_napi_en, &wil->status));
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
wil->status = 0; /* prevent NAPI from being scheduled */
if (test_bit(wil_status_napi_en, &wil->status)) {
napi_synchronize(&wil->napi_rx);
}
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
......@@ -468,7 +493,7 @@ int wil_reset(struct wil6210_priv *wil)
wil->scan_request = NULL;
}
wil6210_disable_irq(wil);
wil_mask_irq(wil);
wmi_event_flush(wil);
......@@ -480,13 +505,38 @@ int wil_reset(struct wil6210_priv *wil)
if (rc)
return rc;
if (!no_fw_load) {
wil_info(wil, "Use firmware <%s>\n", WIL_FW_NAME);
wil_halt_cpu(wil);
/* Loading f/w from the file */
rc = wil_request_firmware(wil, WIL_FW_NAME);
if (rc)
return rc;
/* clear any interrupts which on-card-firmware may have set */
wil6210_clear_irq(wil);
{ /* CAF_ICR - clear and mask */
u32 a = HOSTADDR(RGF_CAF_ICR) +
offsetof(struct RGF_ICR, ICR);
u32 m = HOSTADDR(RGF_CAF_ICR) +
offsetof(struct RGF_ICR, IMV);
u32 icr = ioread32(wil->csr + a);
iowrite32(icr, wil->csr + a); /* W1C */
iowrite32(~0, wil->csr + m);
wmb(); /* wait for completion */
}
wil_release_cpu(wil);
} else {
wil_info(wil, "Use firmware from on-card flash\n");
}
/* init after reset */
wil->pending_connect_cid = -1;
reinit_completion(&wil->wmi_ready);
reinit_completion(&wil->wmi_call);
/* TODO: release MAC reset */
wil6210_enable_irq(wil);
wil_unmask_irq(wil);
/* we just started MAC, wait for FW ready */
rc = wil_wait_for_fw_ready(wil);
......@@ -522,7 +572,7 @@ void wil_link_off(struct wil6210_priv *wil)
netif_carrier_off(ndev);
}
static int __wil_up(struct wil6210_priv *wil)
int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
......@@ -568,11 +618,15 @@ static int __wil_up(struct wil6210_priv *wil)
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
wil_dbg_misc(wil, "NAPI enable\n");
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
set_bit(wil_status_napi_en, &wil->status);
if (wil->platform_ops.bus_request)
wil->platform_ops.bus_request(wil->platform_handle,
WIL_MAX_BUS_REQUEST_KBPS);
return 0;
}
......@@ -580,6 +634,8 @@ int wil_up(struct wil6210_priv *wil)
{
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->mutex);
rc = __wil_up(wil);
mutex_unlock(&wil->mutex);
......@@ -587,13 +643,23 @@ int wil_up(struct wil6210_priv *wil)
return rc;
}
static int __wil_down(struct wil6210_priv *wil)
int __wil_down(struct wil6210_priv *wil)
{
int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
WAIT_FOR_DISCONNECT_INTERVAL_MS;
WARN_ON(!mutex_is_locked(&wil->mutex));
clear_bit(wil_status_napi_en, &wil->status);
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
if (wil->platform_ops.bus_request)
wil->platform_ops.bus_request(wil->platform_handle, 0);
wil_disable_irq(wil);
if (test_and_clear_bit(wil_status_napi_en, &wil->status)) {
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
wil_dbg_misc(wil, "NAPI disable\n");
}
wil_enable_irq(wil);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
......@@ -603,7 +669,24 @@ static int __wil_down(struct wil6210_priv *wil)
wil->scan_request = NULL;
}
wil6210_disconnect(wil, NULL);
if (test_bit(wil_status_fwconnected, &wil->status) ||
test_bit(wil_status_fwconnecting, &wil->status))
wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
/* make sure wil is idle (not connected) */
mutex_unlock(&wil->mutex);
while (iter--) {
int idle = !test_bit(wil_status_fwconnected, &wil->status) &&
!test_bit(wil_status_fwconnecting, &wil->status);
if (idle)
break;
msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
}
mutex_lock(&wil->mutex);
if (!iter)
wil_err(wil, "timeout waiting for idle FW/HW\n");
wil_rx_fini(wil);
return 0;
......@@ -613,6 +696,8 @@ int wil_down(struct wil6210_priv *wil)
{
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->mutex);
rc = __wil_down(wil);
mutex_unlock(&wil->mutex);
......
......@@ -17,11 +17,14 @@
#include <linux/etherdevice.h>
#include "wil6210.h"
#include "txrx.h"
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
wil_dbg_misc(wil, "%s()\n", __func__);
return wil_up(wil);
}
......@@ -29,6 +32,8 @@ static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
wil_dbg_misc(wil, "%s()\n", __func__);
return wil_down(wil);
}
......@@ -36,8 +41,10 @@ static int wil_change_mtu(struct net_device *ndev, int new_mtu)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG)
if (new_mtu < 68 || new_mtu > (TX_BUF_LEN - ETH_HLEN)) {
wil_err(wil, "invalid MTU %d\n", new_mtu);
return -EINVAL;
}
wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu);
ndev->mtu = new_mtu;
......@@ -121,6 +128,8 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
wil->csr = csr;
wil->wdev = wdev;
wil_dbg_misc(wil, "%s()\n", __func__);
rc = wil_priv_init(wil);
if (rc) {
dev_err(dev, "wil_priv_init failed\n");
......@@ -169,6 +178,8 @@ void wil_if_free(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
if (!ndev)
return;
......@@ -185,6 +196,8 @@ int wil_if_add(struct wil6210_priv *wil)
struct net_device *ndev = wil_to_ndev(wil);
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
rc = register_netdev(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
......@@ -200,5 +213,7 @@ void wil_if_remove(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
unregister_netdev(ndev);
}
......@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include "wil6210.h"
......@@ -30,6 +31,28 @@ 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");
void wil_disable_irq(struct wil6210_priv *wil)
{
int irq = wil->pdev->irq;
disable_irq(irq);
if (wil->n_msi == 3) {
disable_irq(irq + 1);
disable_irq(irq + 2);
}
}
void wil_enable_irq(struct wil6210_priv *wil)
{
int irq = wil->pdev->irq;
enable_irq(irq);
if (wil->n_msi == 3) {
enable_irq(irq + 1);
enable_irq(irq + 2);
}
}
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
......@@ -41,6 +64,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
*/
int msi_only = pdev->msi_enabled;
wil_dbg_misc(wil, "%s()\n", __func__);
pdev->msi_enabled = 0;
pci_set_master(pdev);
......@@ -107,6 +132,8 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
wil_dbg_misc(wil, "%s()\n", __func__);
pci_clear_master(pdev);
/* disable and release IRQ */
wil6210_fini_irq(wil, pdev->irq);
......@@ -180,6 +207,10 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil->board = board;
wil6210_clear_irq(wil);
wil->platform_handle =
wil_platform_init(&pdev->dev, &wil->platform_ops);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
......@@ -204,6 +235,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus_disable:
wil_if_pcie_disable(wil);
if_free:
if (wil->platform_ops.uninit)
wil->platform_ops.uninit(wil->platform_handle);
wil_if_free(wil);
err_iounmap:
pci_iounmap(pdev, csr);
......@@ -220,9 +253,13 @@ static void wil_pcie_remove(struct pci_dev *pdev)
struct wil6210_priv *wil = pci_get_drvdata(pdev);
void __iomem *csr = wil->csr;
wil_dbg_misc(wil, "%s()\n", __func__);
wil6210_debugfs_remove(wil);
wil_if_pcie_disable(wil);
wil_if_remove(wil);
wil_if_pcie_disable(wil);
if (wil->platform_ops.uninit)
wil->platform_ops.uninit(wil->platform_handle);
wil_if_free(wil);
pci_iounmap(pdev, csr);
pci_release_region(pdev, 0);
......
......@@ -98,22 +98,25 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
int mid = wil_rxdesc_mid(d);
u16 seq = wil_rxdesc_seq(d);
struct wil_sta_info *sta = &wil->sta[cid];
struct wil_tid_ampdu_rx *r = sta->tid_rx[tid];
struct wil_tid_ampdu_rx *r;
u16 hseq;
int index;
unsigned long flags;
wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
mid, cid, tid, seq);
spin_lock_irqsave(&sta->tid_rx_lock, flags);
r = sta->tid_rx[tid];
if (!r) {
spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
wil_netif_rx_any(skb, ndev);
return;
}
hseq = r->head_seq_num;
spin_lock(&r->reorder_lock);
/** Due to the race between WMI events, where BACK establishment
* reported, and data Rx, few packets may be pass up before reorder
* buffer get allocated. Catch up by pretending SSN is what we
......@@ -176,13 +179,14 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
wil_reorder_release(wil, r);
out:
spin_unlock(&r->reorder_lock);
spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
}
struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
int size, u16 ssn)
{
struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
if (!r)
return NULL;
......@@ -197,7 +201,6 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
return NULL;
}
spin_lock_init(&r->reorder_lock);
r->ssn = ssn;
r->head_seq_num = ssn;
r->buf_size = size;
......
......@@ -52,6 +52,7 @@ static inline int wil_vring_is_full(struct vring *vring)
{
return wil_vring_next_tail(vring) == vring->swhead;
}
/*
* Available space in Tx Vring
*/
......@@ -86,6 +87,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
size_t sz = vring->size * sizeof(vring->va[0]);
uint i;
wil_dbg_misc(wil, "%s()\n", __func__);
BUILD_BUG_ON(sizeof(vring->va[0]) != 32);
vring->swhead = 0;
......@@ -110,7 +113,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
volatile struct vring_tx_desc *_d = &vring->va[i].tx;
_d->dma.status = TX_DMA_STATUS_DU;
}
......@@ -125,6 +129,7 @@ static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
{
dma_addr_t pa = wil_desc_addr(&d->dma.addr);
u16 dmalen = le16_to_cpu(d->dma.length);
switch (ctx->mapped_as) {
case wil_mapped_as_single:
dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
......@@ -143,6 +148,18 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
size_t sz = vring->size * sizeof(vring->va[0]);
if (tx) {
int vring_index = vring - wil->vring_tx;
wil_dbg_misc(wil, "free Tx vring %d [%d] 0x%p:%pad 0x%p\n",
vring_index, vring->size, vring->va,
&vring->pa, vring->ctx);
} else {
wil_dbg_misc(wil, "free Rx vring [%d] 0x%p:%pad 0x%p\n",
vring->size, vring->va,
&vring->pa, vring->ctx);
}
while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
u16 dmalen;
......@@ -191,11 +208,12 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
/* TODO align */
struct sk_buff *skb = dev_alloc_skb(sz + headroom);
if (unlikely(!skb))
return -ENOMEM;
......@@ -274,9 +292,11 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
*/
int len = min_t(int, 8 + sizeof(phy_data),
wil_rxdesc_phy_length(d));
if (len > 8) {
void *p = skb_tail_pointer(skb);
void *pa = PTR_ALIGN(p, 8);
if (skb_tailroom(skb) >= len + (pa - p)) {
phy_length = len - 8;
memcpy(phy_data, pa, phy_length);
......@@ -372,13 +392,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
int cid;
struct wil_net_stats *stats;
BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
if (wil_vring_is_empty(vring))
return NULL;
_d = &(vring->va[vring->swhead].rx);
_d = &vring->va[vring->swhead].rx;
if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
......@@ -532,7 +551,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
[GRO_NORMAL] = "GRO_NORMAL",
[GRO_DROP] = "GRO_DROP",
};
wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
len, gro_res_str[rc]);
}
}
......@@ -573,7 +592,6 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
else
wil_netif_rx_any(skb, ndev);
}
}
wil_rx_refill(wil, v->size);
}
......@@ -583,6 +601,8 @@ int wil_rx_init(struct wil6210_priv *wil)
struct vring *vring = &wil->vring_rx;
int rc;
wil_dbg_misc(wil, "%s()\n", __func__);
if (vring->va) {
wil_err(wil, "Rx ring already allocated\n");
return -EINVAL;
......@@ -612,6 +632,8 @@ void wil_rx_fini(struct wil6210_priv *wil)
{
struct vring *vring = &wil->vring_rx;
wil_dbg_misc(wil, "%s()\n", __func__);
if (vring->va)
wil_vring_free(wil, vring, 0);
}
......@@ -646,6 +668,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
struct vring *vring = &wil->vring_tx[id];
struct vring_tx_data *txdata = &wil->vring_tx_data[id];
wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
rc = -EINVAL;
......@@ -695,6 +720,8 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
if (!vring->va)
return;
wil_dbg_misc(wil, "%s() id=%d\n", __func__, id);
/* make sure NAPI won't touch this vring */
wil->vring_tx_data[id].enabled = 0;
if (test_bit(wil_status_napi_en, &wil->status))
......@@ -721,6 +748,7 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
if (wil->vring2cid_tid[i][0] == cid) {
struct vring *v = &wil->vring_tx[i];
wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
__func__, eth->h_dest, i);
if (v->va) {
......@@ -740,6 +768,7 @@ static void wil_set_da_for_vring(struct wil6210_priv *wil,
{
struct ethhdr *eth = (void *)skb->data;
int cid = wil->vring2cid_tid[vring_index][0];
memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
}
......@@ -750,7 +779,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
* duplicate skb and send it to other active vrings
*/
static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
struct sk_buff *skb)
struct sk_buff *skb)
{
struct vring *v, *v2;
struct sk_buff *skb2;
......@@ -833,8 +862,8 @@ void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
}
static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
struct vring_tx_desc *d,
struct sk_buff *skb)
struct vring_tx_desc *d,
struct sk_buff *skb)
{
int protocol;
......@@ -902,10 +931,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags);
return -ENOMEM;
}
_d = &(vring->va[i].tx);
_d = &vring->va[i].tx;
pa = dma_map_single(dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
skb->data, &pa);
......@@ -934,10 +962,11 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size;
_d = &(vring->va[i].tx);
_d = &vring->va[i].tx;
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE);
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
vring->ctx[i].mapped_as = wil_mapped_as_page;
......@@ -982,7 +1011,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
i = (swhead + f) % vring->size;
ctx = &vring->ctx[i];
_d = &(vring->va[i].tx);
_d = &vring->va[i].tx;
*d = *_d;
_d->dma.status = TX_DMA_STATUS_DU;
wil_txdesc_unmap(dev, d, ctx);
......@@ -996,7 +1025,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
return -EINVAL;
}
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
......@@ -1024,15 +1052,15 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
pr_once_fw = false;
/* find vring */
if (is_unicast_ether_addr(eth->h_dest)) {
if (is_unicast_ether_addr(eth->h_dest))
vring = wil_find_tx_vring(wil, skb);
} else {
else
vring = wil_tx_bcast(wil, skb);
}
if (!vring) {
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);
......
......@@ -20,9 +20,9 @@
#define BUF_SW_OWNED (1)
#define BUF_HW_OWNED (0)
/* size of max. Rx packet */
#define RX_BUF_LEN (2048)
#define TX_BUF_LEN (2048)
/* size of max. Tx/Rx buffers, as supported by FW */
#define RX_BUF_LEN (2242)
#define TX_BUF_LEN (2242)
/* how many bytes to reserve for rtap header? */
#define WIL6210_RTAP_SIZE (128)
......@@ -237,7 +237,6 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
......@@ -246,7 +245,6 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
#define TX_DMA_STATUS_DU BIT(0)
struct vring_tx_dma {
......@@ -347,7 +345,6 @@ struct vring_rx_mac {
#define RX_DMA_ERROR_L3_ERR BIT(4)
#define RX_DMA_ERROR_L4_ERR BIT(5)
/* Status field */
#define RX_DMA_STATUS_DU BIT(0)
#define RX_DMA_STATUS_ERROR BIT(2)
......
......@@ -21,8 +21,13 @@
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <linux/timex.h>
#include "wil_platform.h"
#define WIL_NAME "wil6210"
#define WIL_FW_NAME "wil6210.fw"
#define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */
struct wil_board {
int board;
......@@ -86,22 +91,29 @@ struct RGF_ICR {
/* registers - FW addresses */
#define RGF_USER_USAGE_1 (0x880004)
#define RGF_USER_USAGE_6 (0x880018)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
#define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
#define RGF_USER_CLKS_CTL_0 (0x880abc)
#define BIT_USER_CLKS_CAR_AHB_SW_SEL BIT(1) /* ref clk/PLL */
#define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
#define BIT_HPAL_PERST_FROM_PAD BIT(6)
#define BIT_CAR_PERST_RST BIT(7)
#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0 (0x880c18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
......@@ -136,6 +148,8 @@ struct RGF_ICR {
/* MAC timer, usec, for packet lifetime */
#define RGF_MAC_MTRL_COUNTER_0 (0x886aa8)
#define RGF_CAF_ICR (0x88946c) /* struct RGF_ICR */
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
......@@ -154,6 +168,7 @@ struct fw_map {
u32 host; /* PCI/Host address - BAR0 + 0x880000 */
const char *name; /* for debugfs */
};
/* array size should be in sync with actual definition in the wmi.c */
extern const struct fw_map fw_mapping[7];
......@@ -303,18 +318,12 @@ struct pci_dev;
* @timeout: reset timer value (in TUs).
* @dialog_token: dialog token for aggregation session
* @rcu_head: RCU head used for freeing this struct
* @reorder_lock: serializes access to reorder buffer, see below.
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
*
* The @reorder_lock is used to protect the members of this
* struct, except for @timeout, @buf_size and @dialog_token,
* which are constant across the lifetime of the struct (the
* dialog token being used only for debugging).
*/
struct wil_tid_ampdu_rx {
spinlock_t reorder_lock; /* see above */
struct sk_buff **reorder_buf;
unsigned long *reorder_time;
struct timer_list session_timer;
......@@ -363,6 +372,7 @@ struct wil_sta_info {
bool data_port_open; /* can send any data, not only EAPOL */
/* Rx BACK */
struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM];
spinlock_t tid_rx_lock; /* guarding tid_rx array */
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
};
......@@ -389,6 +399,7 @@ struct wil6210_priv {
struct mutex wmi_mutex;
struct wil6210_mbox_ctl mbox_ctl;
struct completion wmi_ready;
struct completion wmi_call;
u16 wmi_seq;
u16 reply_id; /**< wait for this WMI event */
void *reply_buf;
......@@ -426,6 +437,9 @@ struct wil6210_priv {
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
void *platform_handle;
struct wil_platform_ops platform_ops;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
......@@ -435,10 +449,11 @@ struct wil6210_priv {
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
#define wil_to_pcie_dev(i) (&i->pdev->dev)
int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
wil_dbg_trace(wil, fmt, ##arg); \
......@@ -449,6 +464,7 @@ int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg)
#if defined(CONFIG_DYNAMIC_DEBUG)
#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
print_hex_dump_debug("DBG[TXRX]" prefix_str,\
......@@ -460,6 +476,19 @@ int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
print_hex_dump_debug("DBG[ WMI]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
#else /* defined(CONFIG_DYNAMIC_DEBUG) */
static inline
void wil_hex_dump_txrx(const char *prefix_str, int prefix_type, int rowsize,
int groupsize, const void *buf, size_t len, bool ascii)
{
}
static inline
void wil_hex_dump_wmi(const char *prefix_str, int prefix_type, int rowsize,
int groupsize, const void *buf, size_t len, bool ascii)
{
}
#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
size_t count);
......@@ -477,7 +506,9 @@ void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
int __wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
int __wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
......@@ -510,8 +541,10 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
void wil6210_clear_irq(struct wil6210_priv *wil);
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);
void wil_mask_irq(struct wil6210_priv *wil);
void wil_unmask_irq(struct wil6210_priv *wil);
void wil_disable_irq(struct wil6210_priv *wil);
void wil_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);
......@@ -547,4 +580,5 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
int wil_request_firmware(struct wil6210_priv *wil, const char *name);
#endif /* __WIL6210_H__ */
/*
* 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 "linux/device.h"
#include "wil_platform.h"
#ifdef CONFIG_WIL6210_PLATFORM_MSM
#include "wil_platform_msm.h"
#endif
/**
* wil_platform_init() - wil6210 platform module init
*
* The function must be called before all other functions in this module.
* It returns a handle which is used with the rest of the API
*
*/
void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops)
{
void *handle = NULL;
if (!ops) {
dev_err(dev, "Invalid parameter. Cannot init platform module\n");
return NULL;
}
#ifdef CONFIG_WIL6210_PLATFORM_MSM
handle = wil_platform_msm_init(dev, ops);
if (handle)
return handle;
#endif
/* other platform specific init functions should be called here */
return handle;
}
/*
* 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.
*/
#ifndef __WIL_PLATFORM_H__
#define __WIL_PLATFORM_H__
struct device;
/**
* struct wil_platform_ops - wil platform module callbacks
*/
struct wil_platform_ops {
int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */);
int (*suspend)(void *handle);
int (*resume)(void *handle);
void (*uninit)(void *handle);
};
void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops);
#endif /* __WIL_PLATFORM_H__ */
/*
* 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 <linux/of.h>
#include <linux/slab.h>
#include <linux/msm-bus.h>
#include "wil_platform.h"
#include "wil_platform_msm.h"
/**
* struct wil_platform_msm - wil6210 msm platform module info
*
* @dev: device object
* @msm_bus_handle: handle for using msm_bus API
* @pdata: bus scale info retrieved from DT
*/
struct wil_platform_msm {
struct device *dev;
uint32_t msm_bus_handle;
struct msm_bus_scale_pdata *pdata;
};
#define KBTOB(a) (a * 1000ULL)
/**
* wil_platform_get_pdata() - Generate bus client data from device tree
* provided by clients.
*
* dev: device object
* of_node: Device tree node to extract information from
*
* The function returns a valid pointer to the allocated bus-scale-pdata
* if the vectors were correctly read from the client's device node.
* Any error in reading or parsing the device node will return NULL
* to the caller.
*/
static struct msm_bus_scale_pdata *wil_platform_get_pdata(
struct device *dev,
struct device_node *of_node)
{
struct msm_bus_scale_pdata *pdata;
struct msm_bus_paths *usecase;
int i, j, ret, len;
unsigned int num_usecases, num_paths, mem_size;
const uint32_t *vec_arr;
struct msm_bus_vectors *vectors;
/* first read num_usecases and num_paths so we can calculate
* amount of memory to allocate
*/
ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
&num_usecases);
if (ret) {
dev_err(dev, "Error: num-usecases not found\n");
return NULL;
}
ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
&num_paths);
if (ret) {
dev_err(dev, "Error: num_paths not found\n");
return NULL;
}
/* pdata memory layout:
* msm_bus_scale_pdata
* msm_bus_paths[num_usecases]
* msm_bus_vectors[num_usecases][num_paths]
*/
mem_size = sizeof(struct msm_bus_scale_pdata) +
sizeof(struct msm_bus_paths) * num_usecases +
sizeof(struct msm_bus_vectors) * num_usecases * num_paths;
pdata = kzalloc(mem_size, GFP_KERNEL);
if (!pdata)
return NULL;
ret = of_property_read_string(of_node, "qcom,msm-bus,name",
&pdata->name);
if (ret) {
dev_err(dev, "Error: Client name not found\n");
goto err;
}
if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) {
pdata->active_only = 1;
} else {
dev_info(dev, "active_only flag absent.\n");
dev_info(dev, "Using dual context by default\n");
}
pdata->num_usecases = num_usecases;
pdata->usecase = (struct msm_bus_paths *)(pdata + 1);
vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
if (vec_arr == NULL) {
dev_err(dev, "Error: Vector array not found\n");
goto err;
}
if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
dev_err(dev, "Error: Length-error on getting vectors\n");
goto err;
}
vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases);
for (i = 0; i < num_usecases; i++) {
usecase = &pdata->usecase[i];
usecase->num_paths = num_paths;
usecase->vectors = &vectors[i];
for (j = 0; j < num_paths; j++) {
int index = ((i * num_paths) + j) * 4;
usecase->vectors[j].src = be32_to_cpu(vec_arr[index]);
usecase->vectors[j].dst =
be32_to_cpu(vec_arr[index + 1]);
usecase->vectors[j].ab = (uint64_t)
KBTOB(be32_to_cpu(vec_arr[index + 2]));
usecase->vectors[j].ib = (uint64_t)
KBTOB(be32_to_cpu(vec_arr[index + 3]));
}
}
return pdata;
err:
kfree(pdata);
return NULL;
}
/* wil_platform API (callbacks) */
static int wil_platform_bus_request(void *handle,
uint32_t kbps /* KBytes/Sec */)
{
int rc, i;
struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */
struct msm_bus_paths *usecase;
uint32_t usecase_kbps;
uint32_t min_kbps = ~0;
/* find the lowest usecase that is bigger than requested kbps */
for (i = 0; i < msm->pdata->num_usecases; i++) {
usecase = &msm->pdata->usecase[i];
/* assume we have single path (vectors[0]). If we ever
* have multiple paths, need to define the behavior */
usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000);
if (usecase_kbps >= kbps && usecase_kbps < min_kbps) {
min_kbps = usecase_kbps;
vote = i;
}
}
rc = msm_bus_scale_client_update_request(msm->msm_bus_handle, vote);
if (rc)
dev_err(msm->dev, "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n",
kbps, vote, rc);
else
/* TOOD: remove */
dev_info(msm->dev, "msm_bus_scale_client_update_request succeeded. kbps=%d vote=%d\n",
kbps, vote);
return rc;
}
static void wil_platform_uninit(void *handle)
{
struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
dev_info(msm->dev, "wil_platform_uninit\n");
if (msm->msm_bus_handle)
msm_bus_scale_unregister_client(msm->msm_bus_handle);
kfree(msm->pdata);
kfree(msm);
}
static int wil_platform_msm_bus_register(struct wil_platform_msm *msm,
struct device_node *node)
{
msm->pdata = wil_platform_get_pdata(msm->dev, node);
if (!msm->pdata) {
dev_err(msm->dev, "Failed getting DT info\n");
return -EINVAL;
}
msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata);
if (!msm->msm_bus_handle) {
dev_err(msm->dev, "Failed msm_bus registration\n");
return -EINVAL;
}
dev_info(msm->dev, "msm_bus registration succeeded! handle 0x%x\n",
msm->msm_bus_handle);
return 0;
}
/**
* wil_platform_msm_init() - wil6210 msm platform module init
*
* The function must be called before all other functions in this module.
* It returns a handle which is used with the rest of the API
*
*/
void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops)
{
struct device_node *of_node;
struct wil_platform_msm *msm;
int rc;
of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210");
if (!of_node) {
/* this could mean non-msm platform */
dev_err(dev, "DT node not found\n");
return NULL;
}
msm = kzalloc(sizeof(*msm), GFP_KERNEL);
if (!msm)
return NULL;
msm->dev = dev;
/* register with msm_bus module for scaling requests */
rc = wil_platform_msm_bus_register(msm, of_node);
if (rc)
goto cleanup;
memset(ops, 0, sizeof(*ops));
ops->bus_request = wil_platform_bus_request;
ops->uninit = wil_platform_uninit;
return (void *)msm;
cleanup:
kfree(msm);
return NULL;
}
/*
* 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.
*/
#ifndef __WIL_PLATFORM__MSM_H__
#define __WIL_PLATFORM_MSM_H__
#include "wil_platform.h"
void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops);
#endif /* __WIL_PLATFORM__MSM_H__ */
......@@ -157,6 +157,7 @@ int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
struct wil6210_mbox_hdr *hdr)
{
void __iomem *src = wmi_buffer(wil, ptr);
if (!src)
return -EINVAL;
......@@ -278,6 +279,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct wmi_ready_event *evt = d;
wil->fw_version = le32_to_cpu(evt->sw_version);
wil->n_mids = evt->numof_additional_mids;
......@@ -298,7 +300,7 @@ static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
wil_dbg_wmi(wil, "WMI: got FW ready event\n");
set_bit(wil_status_fwready, &wil->status);
/* reuse wmi_ready for the firmware ready indication */
/* let the reset sequence continue */
complete(&wil->wmi_ready);
}
......@@ -595,27 +597,40 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
return;
}
mutex_lock(&wil->mutex);
cid = wil->vring2cid_tid[evt->ringid][0];
if (cid >= WIL6210_MAX_CID) {
wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
return;
goto out;
}
sta = &wil->sta[cid];
if (sta->status == wil_sta_unused) {
wil_err(wil, "CID %d unused\n", cid);
return;
goto out;
}
wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
for (i = 0; i < WIL_STA_TID_NUM; i++) {
struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
struct wil_tid_ampdu_rx *r;
unsigned long flags;
spin_lock_irqsave(&sta->tid_rx_lock, flags);
r = sta->tid_rx[i];
sta->tid_rx[i] = NULL;
wil_tid_ampdu_rx_free(wil, r);
spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
evt->agg_wsize, 0);
}
out:
mutex_unlock(&wil->mutex);
}
static const struct {
......@@ -653,7 +668,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
unsigned n;
if (!test_bit(wil_status_reset_done, &wil->status)) {
wil_err(wil, "Reset not completed\n");
wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
return;
}
......@@ -708,6 +723,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->id);
u32 tstamp = le32_to_cpu(wmi->timestamp);
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
id, wmi->mid, tstamp);
trace_wil6210_wmi_event(wmi, &wmi[1],
......@@ -748,8 +764,8 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
wil->reply_id = reply_id;
wil->reply_buf = reply;
wil->reply_size = reply_size;
remain = wait_for_completion_timeout(&wil->wmi_ready,
msecs_to_jiffies(to_msec));
remain = wait_for_completion_timeout(&wil->wmi_call,
msecs_to_jiffies(to_msec));
if (0 == remain) {
wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n",
cmdid, reply_id, to_msec);
......@@ -953,8 +969,11 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
int rc;
u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
if (!ie)
ie_len = 0;
cmd->mgmt_frm_type = type;
/* BUG: FW API define ieLen as u8. Will fix FW */
......@@ -1128,6 +1147,9 @@ static void wmi_event_handle(struct wil6210_priv *wil,
struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->id);
wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
id, wil->reply_id);
/* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id) {
if (wil->reply_buf) {
......@@ -1138,7 +1160,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
len - sizeof(*wmi));
}
wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
complete(&wil->wmi_ready);
complete(&wil->wmi_call);
return;
}
/* unsolicited event */
......@@ -1184,9 +1206,11 @@ void wmi_event_worker(struct work_struct *work)
struct pending_wmi_event *evt;
struct list_head *lh;
wil_dbg_wmi(wil, "Start %s\n", __func__);
while ((lh = next_wmi_ev(wil)) != NULL) {
evt = list_entry(lh, struct pending_wmi_event, list);
wmi_event_handle(wil, &evt->event.hdr);
kfree(evt);
}
wil_dbg_wmi(wil, "Finished %s\n", __func__);
}
......@@ -179,7 +179,6 @@ enum wmi_crypto_type {
WMI_CRYPT_AES_GCMP = 0x20,
};
enum wmi_connect_ctrl_flag_bits {
WMI_CONNECT_ASSOC_POLICY_USER = 0x0001,
WMI_CONNECT_SEND_REASSOC = 0x0002,
......@@ -219,7 +218,6 @@ struct wmi_disconnect_sta_cmd {
__le16 disconnect_reason;
} __packed;
/*
* WMI_SET_PMK_CMDID
*/
......@@ -234,7 +232,6 @@ struct wmi_set_pmk_cmd {
u8 pmk[WMI_PMK_LEN];
} __packed;
/*
* WMI_SET_PASSPHRASE_CMDID
*/
......@@ -273,7 +270,6 @@ struct wmi_delete_cipher_key_cmd {
u8 mac[WMI_MAC_LEN];
} __packed;
/*
* WMI_START_SCAN_CMDID
*
......@@ -325,7 +321,6 @@ struct wmi_probed_ssid_cmd {
u8 ssid[WMI_MAX_SSID_LEN];
} __packed;
/*
* WMI_SET_APPIE_CMDID
* Add Application specified IE to a management frame
......@@ -351,7 +346,6 @@ struct wmi_set_appie_cmd {
u8 ie_info[0];
} __packed;
/*
* WMI_PXMT_RANGE_CFG_CMDID
*/
......@@ -380,7 +374,6 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
/*
* WMI_RF_RX_TEST_CMDID
*/
......@@ -426,7 +419,6 @@ struct wmi_bcon_ctrl_cmd {
u8 disable_sec;
} __packed;
/******* P2P ***********/
/*
......@@ -797,7 +789,6 @@ struct wmi_temp_sense_cmd {
__le32 measure_marlon_r_en;
} __packed;
/*
* WMI Events
*/
......@@ -887,7 +878,6 @@ enum wmi_event_id {
* Events data structures
*/
enum wmi_fw_status {
WMI_FW_STATUS_SUCCESS,
WMI_FW_STATUS_FAILURE,
......@@ -1038,8 +1028,8 @@ struct wmi_disconnect_event {
__le16 protocol_reason_status; /* reason code, see 802.11 spec. */
u8 bssid[WMI_MAC_LEN]; /* set if known */
u8 disconnect_reason; /* see wmi_disconnect_reason */
u8 assoc_resp_len; /* not in use */
u8 assoc_info[0]; /* not in use */
u8 assoc_resp_len; /* not used */
u8 assoc_info[0]; /* not used */
} __packed;
/*
......@@ -1081,7 +1071,6 @@ struct wmi_delba_event {
__le16 reason;
} __packed;
/*
* WMI_VRING_CFG_DONE_EVENTID
*/
......@@ -1147,7 +1136,6 @@ struct wmi_data_port_open_event {
u8 reserved[3];
} __packed;
/*
* WMI_GET_PCP_CHANNEL_EVENTID
*/
......@@ -1156,7 +1144,6 @@ struct wmi_get_pcp_channel_event {
u8 reserved[3];
} __packed;
/*
* WMI_PORT_ALLOCATED_EVENTID
*/
......@@ -1260,7 +1247,6 @@ struct wmi_rx_mgmt_info {
u8 channel; /* From Radio MNGR */
} __packed;
/*
* WMI_TX_MGMT_PACKET_EVENTID
*/
......
......@@ -45,6 +45,7 @@
#define B43_MMIO_RAM_DATA 0x134
#define B43_MMIO_PS_STATUS 0x140
#define B43_MMIO_RADIO_HWENABLED_HI 0x158
#define B43_MMIO_MAC_HW_CAP 0x15C /* MAC capabilities (corerev >= 13) */
#define B43_MMIO_SHM_CONTROL 0x160
#define B43_MMIO_SHM_DATA 0x164
#define B43_MMIO_SHM_DATA_UNALIGNED 0x166
......@@ -253,6 +254,8 @@ enum {
#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */
#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5 Ghz channel */
#define B43_SHM_SH_CHAN_40MHZ 0x0200 /* Bit set, if 40 Mhz channel width */
#define B43_SHM_SH_MACHW_L 0x00C0 /* Location where the ucode expects the MAC capabilities */
#define B43_SHM_SH_MACHW_H 0x00C2 /* Location where the ucode expects the MAC capabilities */
#define B43_SHM_SH_HOSTF5 0x00D4 /* Hostflags 5 for ucode options */
#define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */
/* TSSI information */
......@@ -297,6 +300,7 @@ enum {
#define B43_SHM_SH_LFFBLIM 0x0046 /* Long frame fallback retry limit */
#define B43_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word (see PHY TX control) */
#define B43_SHM_SH_EXTNPHYCTL 0x00B0 /* Extended bytes for beacon PHY control (N) */
#define B43_SHM_SH_BCN_LI 0x00B6 /* beacon listen interval */
/* SHM_SHARED ACK/CTS control */
#define B43_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word (see PHY TX control) */
/* SHM_SHARED probe response variables */
......@@ -476,6 +480,11 @@ enum {
#define B43_MACCMD_CCA 0x00000008 /* Clear channel assessment */
#define B43_MACCMD_BGNOISE 0x00000010 /* Background noise */
/* B43_MMIO_PSM_PHY_HDR bits */
#define B43_PSM_HDR_MAC_PHY_RESET 0x00000001
#define B43_PSM_HDR_MAC_PHY_CLOCK_EN 0x00000002
#define B43_PSM_HDR_MAC_PHY_FORCE_CLK 0x00000004
/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */
#define B43_BCMA_CLKCTLST_80211_PLL_REQ 0x00000100
#define B43_BCMA_CLKCTLST_PHY_PLL_REQ 0x00000200
......
此差异已折叠。
......@@ -96,6 +96,8 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason);
#define B43_PS_ASLEEP (1 << 3) /* Force device asleep */
void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags);
void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev);
void b43_mac_suspend(struct b43_wldev *dev);
void b43_mac_enable(struct b43_wldev *dev);
void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);
......
此差异已折叠。
......@@ -81,7 +81,9 @@
#define B43_PHY_HT_RF_SEQ_STATUS B43_PHY_EXTG(0x004)
/* Values for the status are the same as for the trigger */
#define B43_PHY_HT_RF_CTL1 B43_PHY_EXTG(0x010)
#define B43_PHY_HT_RF_CTL_CMD 0x810
#define B43_PHY_HT_RF_CTL_CMD_FORCE 0x0001
#define B43_PHY_HT_RF_CTL_CMD_CHIP0_PU 0x0002
#define B43_PHY_HT_RF_CTL_INT_C1 B43_PHY_EXTG(0x04c)
#define B43_PHY_HT_RF_CTL_INT_C2 B43_PHY_EXTG(0x06c)
......@@ -104,6 +106,9 @@
#define B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT 0
#define B43_PHY_HT_TX_PCTL_STATUS_C3 B43_PHY_EXTG(0x169)
#define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001)
#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */
#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */
#define B43_PHY_HT_TEST B43_PHY_N_BMODE(0x00A)
......
......@@ -6369,7 +6369,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
b43_mac_switch_freq(dev, spuravoid);
if (dev->phy.rev == 3 || dev->phy.rev == 4)
; /* TODO: reset PLL */
b43_wireless_core_phy_pll_reset(dev);
if (spuravoid)
b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册