Major changes:

iwlwifi:
 
 * more work for new devices (4165 / 8260)
 * cleanups / improvemnts in rate control
 * fixes for TDLS
 * major statistics work from Johannes - more to come
 * improvements for the fw error dump infrastructure
 * usual amount of small fixes here and there (scan, D0i3 etc...)
 * add support for beamforming
 * enable stuck queue detection for iwlmvm
 * a few fixes for EBS scan
 * fixes for various failure paths
 * improvements for TDLS Offchannel
 
 wil6210:
 
 * performance tuning
 * some AP features
 
 brcm80211:
 
 * rework some code in SDIO part of the brcmfmac driver related to
   suspend/resume that were found doing stress testing
 * in PCIe part scheduling of worker thread needed to be relaxed
 * minor fixes and exposing firmware revision information to
   user-space, ie. ethtool.
 
 mwifiex:
 
 * enhancements for change virtual interface handling
 * remove coupling between netdev and FW supported interface
   combination, now conversion from any type of supported interface
   types to any other type is possible
 * DFS support in AP mode
 
 ath9k:
 
 * fix calibration issues on some boards
 * Wake-on-WLAN improvements
 
 ath10k:
 
 * add support for qca6174 hardware
 * enable RX batching to reduce CPU load
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQEcBAABAgAGBQJU1fUoAAoJEG4XJFUm622bnjMH/25f2kyLAJSLJmiIhpEcYlNJ
 CJMAYsSTcqhKaMEbx742StUUDH3pa1oV4mQ5csVa2QJIsS90WzPpB1eRnNtQ69Nj
 89Zfwa6bbX0TXDgw1Aa28NewPY/3xWpCjI03HBQaMIncToWv3/dzNUr0bEmIuBds
 wsr5Y+fy80VKnkoXG7XzGFOqmxxFwNS+UF3M1WNtQ+xcr9rK3/LLxyWFy84S9UIe
 4lVOb+df91YFIJeLs28/hfTiRhvV0fWIbupGv8UhuBMho+F0dpLvra+3xksEqALu
 4+AzI0j9GjqFfMzbmRBcPcT4Rl37nLmvQ+7XokrFgAYS5zp7OGylOMU3nHyvkuY=
 =iL8v
 -----END PGP SIGNATURE-----

Merge tag 'wireless-drivers-next-for-davem-2015-02-07' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next

Major changes:

iwlwifi:

* more work for new devices (4165 / 8260)
* cleanups / improvemnts in rate control
* fixes for TDLS
* major statistics work from Johannes - more to come
* improvements for the fw error dump infrastructure
* usual amount of small fixes here and there (scan, D0i3 etc...)
* add support for beamforming
* enable stuck queue detection for iwlmvm
* a few fixes for EBS scan
* fixes for various failure paths
* improvements for TDLS Offchannel

wil6210:

* performance tuning
* some AP features

brcm80211:

* rework some code in SDIO part of the brcmfmac driver related to
  suspend/resume that were found doing stress testing
* in PCIe part scheduling of worker thread needed to be relaxed
* minor fixes and exposing firmware revision information to
  user-space, ie. ethtool.

mwifiex:

* enhancements for change virtual interface handling
* remove coupling between netdev and FW supported interface
  combination, now conversion from any type of supported interface
  types to any other type is possible
* DFS support in AP mode

ath9k:

* fix calibration issues on some boards
* Wake-on-WLAN improvements

ath10k:

* add support for qca6174 hardware
* enable RX batching to reduce CPU load

Conflicts:
	drivers/net/wireless/rtlwifi/pci.c

Conflict resolution is to get rid of the 'end' label and keep
the rest.

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2015-02-09 12:07:20 -08:00
commit c8ac18f200
173 changed files with 6480 additions and 2200 deletions

View file

@ -107,6 +107,14 @@ extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc);
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
#else
static inline bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
{
return false;
}
static inline void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
{
}
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
#ifdef CONFIG_BCMA_DRIVER_GPIO

View file

@ -178,7 +178,6 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
{
u32 maxt;
enum bcma_clkmode clkmode;
maxt = bcma_chipco_watchdog_get_max_timer(cc);
if (cc->capabilities & BCMA_CC_CAP_PMU) {
@ -188,8 +187,13 @@ u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
ticks = maxt;
bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
} else {
clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC;
bcma_core_set_clockmode(cc->core, clkmode);
struct bcma_bus *bus = cc->core->bus;
if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 &&
bus->chipinfo.id != BCMA_CHIP_ID_BCM53018)
bcma_core_set_clockmode(cc->core,
ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC);
if (ticks > maxt)
ticks = maxt;
/* instant NMI */

View file

@ -144,6 +144,47 @@ static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device,
return bcma_pcie_mdio_read(pc, device, address);
}
/**************************************************
* Early init.
**************************************************/
static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
{
struct bcma_device *core = pc->core;
u16 val16, core_index;
uint regoff;
regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
core_index = (u16)core->core_index;
val16 = pcicore_read16(pc, regoff);
if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
!= core_index) {
val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
pcicore_write16(pc, regoff, val16);
}
}
/*
* Apply some early fixes required before accessing SPROM.
* See also si_pci_fixcfg.
*/
void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
{
if (pc->early_setup_done)
return;
pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
if (pc->hostmode)
goto out;
bcma_core_pci_fixcfg(pc);
out:
pc->early_setup_done = true;
}
/**************************************************
* Workarounds.
**************************************************/
@ -175,24 +216,6 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
}
static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
{
struct bcma_device *core = pc->core;
u16 val16, core_index;
uint regoff;
regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
core_index = (u16)core->core_index;
val16 = pcicore_read16(pc, regoff);
if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
!= core_index) {
val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
pcicore_write16(pc, regoff, val16);
}
}
/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
/* Needs to happen when coming out of 'standby'/'hibernate' */
static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
@ -216,7 +239,6 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
{
bcma_core_pci_fixcfg(pc);
bcma_pcicore_serdes_workaround(pc);
bcma_core_pci_config_fixup(pc);
}
@ -226,13 +248,11 @@ void bcma_core_pci_init(struct bcma_drv_pci *pc)
if (pc->setup_done)
return;
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
bcma_core_pci_early_init(pc);
if (pc->hostmode)
bcma_core_pci_hostmode_init(pc);
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
if (!pc->hostmode)
else
bcma_core_pci_clientmode_init(pc);
}

View file

@ -13,10 +13,12 @@
static void bcma_host_pci_switch_core(struct bcma_device *core)
{
int win2 = core->bus->host_is_pcie2 ?
BCMA_PCIE2_BAR0_WIN2 : BCMA_PCI_BAR0_WIN2;
pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN,
core->addr);
pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
core->wrap);
pci_write_config_dword(core->bus->host_pci, win2, core->wrap);
core->bus->mapped_core = core;
bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id);
}

View file

@ -368,12 +368,19 @@ static void bcma_unregister_cores(struct bcma_bus *bus)
struct bcma_device *core, *tmp;
list_for_each_entry_safe(core, tmp, &bus->cores, list) {
if (!core->dev_registered)
continue;
list_del(&core->list);
if (core->dev_registered)
device_unregister(&core->dev);
device_unregister(&core->dev);
}
if (bus->hosttype == BCMA_HOSTTYPE_SOC)
platform_device_unregister(bus->drv_cc.watchdog);
/* Now noone uses internally-handled cores, we can free them */
list_for_each_entry_safe(core, tmp, &bus->cores, list) {
list_del(&core->list);
kfree(core);
}
}
int bcma_bus_register(struct bcma_bus *bus)
@ -395,6 +402,13 @@ int bcma_bus_register(struct bcma_bus *bus)
bcma_core_chipcommon_early_init(&bus->drv_cc);
}
/* Early init PCIE core */
core = bcma_find_core(bus, BCMA_CORE_PCIE);
if (core) {
bus->drv_pci[0].core = core;
bcma_core_pci_early_init(&bus->drv_pci[0]);
}
/* 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))
@ -467,7 +481,6 @@ int bcma_bus_register(struct bcma_bus *bus)
void bcma_bus_unregister(struct bcma_bus *bus)
{
struct bcma_device *cores[3];
int err;
err = bcma_gpio_unregister(&bus->drv_cc);
@ -478,15 +491,7 @@ void bcma_bus_unregister(struct bcma_bus *bus)
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);
bcma_unregister_cores(bus);
kfree(cores[2]);
kfree(cores[1]);
kfree(cores[0]);
}
/*

View file

@ -579,7 +579,8 @@ int bcma_sprom_get(struct bcma_bus *bus)
u16 offset = BCMA_CC_SPROM;
u16 *sprom;
size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
SSB_SPROMSIZE_WORDS_R10, };
SSB_SPROMSIZE_WORDS_R10,
SSB_SPROMSIZE_WORDS_R11, };
int i, err = 0;
if (!bus->drv_cc.core)

View file

@ -64,6 +64,7 @@ enum ath_op_flags {
ATH_OP_HW_RESET,
ATH_OP_SCANNING,
ATH_OP_MULTI_CHANNEL,
ATH_OP_WOW_ENABLED,
};
enum ath_bus_type {

View file

@ -9,12 +9,14 @@ ath10k_core-y += mac.o \
txrx.o \
wmi.o \
wmi-tlv.o \
bmi.o
bmi.o \
hw.o
ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o
ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
ath10k_core-$(CONFIG_THERMAL) += thermal.o
ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o
obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
ath10k_pci-y += pci.o \

View file

@ -803,7 +803,7 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar)
int ce_id;
for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
u32 ctrl_addr = ath10k_ce_base_address(ce_id);
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
ath10k_ce_error_intr_disable(ar, ctrl_addr);
@ -832,7 +832,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
nentries = roundup_pow_of_two(attr->src_nentries);
@ -869,7 +869,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
nentries = roundup_pow_of_two(attr->dest_nentries);
@ -1051,7 +1051,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
{
u32 ctrl_addr = ath10k_ce_base_address(ce_id);
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0);
ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0);
@ -1061,7 +1061,7 @@ static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id)
{
u32 ctrl_addr = ath10k_ce_base_address(ce_id);
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0);
ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0);
@ -1098,7 +1098,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
ce_state->ar = ar;
ce_state->id = ce_id;
ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
ce_state->ctrl_addr = ath10k_ce_base_address(ar, ce_id);
ce_state->attr_flags = attr->flags;
ce_state->src_sz_max = attr->src_sz_max;

View file

@ -394,7 +394,7 @@ struct ce_attr {
#define DST_WATERMARK_HIGH_RESET 0
#define DST_WATERMARK_ADDRESS 0x0050
static inline u32 ath10k_ce_base_address(unsigned int ce_id)
static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
{
return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
}

View file

@ -57,6 +57,49 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.board_ext_size = QCA988X_BOARD_EXT_DATA_SZ,
},
},
{
.id = QCA6174_HW_2_1_VERSION,
.name = "qca6174 hw2.1",
.patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
.uart_pin = 6,
.fw = {
.dir = QCA6174_HW_2_1_FW_DIR,
.fw = QCA6174_HW_2_1_FW_FILE,
.otp = QCA6174_HW_2_1_OTP_FILE,
.board = QCA6174_HW_2_1_BOARD_DATA_FILE,
.board_size = QCA6174_BOARD_DATA_SZ,
.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
},
},
{
.id = QCA6174_HW_3_0_VERSION,
.name = "qca6174 hw3.0",
.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
.uart_pin = 6,
.fw = {
.dir = QCA6174_HW_3_0_FW_DIR,
.fw = QCA6174_HW_3_0_FW_FILE,
.otp = QCA6174_HW_3_0_OTP_FILE,
.board = QCA6174_HW_3_0_BOARD_DATA_FILE,
.board_size = QCA6174_BOARD_DATA_SZ,
.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
},
},
{
.id = QCA6174_HW_3_2_VERSION,
.name = "qca6174 hw3.2",
.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
.uart_pin = 6,
.fw = {
/* uses same binaries as hw3.0 */
.dir = QCA6174_HW_3_0_FW_DIR,
.fw = QCA6174_HW_3_0_FW_FILE,
.otp = QCA6174_HW_3_0_OTP_FILE,
.board = QCA6174_HW_3_0_BOARD_DATA_FILE,
.board_size = QCA6174_BOARD_DATA_SZ,
.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
},
},
};
static void ath10k_send_suspend_complete(struct ath10k *ar)
@ -927,6 +970,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
case ATH10K_FW_WMI_OP_VERSION_TLV:
ar->max_num_peers = TARGET_TLV_NUM_PEERS;
ar->max_num_stations = TARGET_TLV_NUM_STATIONS;
ar->max_num_vdevs = TARGET_TLV_NUM_VDEVS;
ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC;
break;
case ATH10K_FW_WMI_OP_VERSION_UNSET:
@ -1060,6 +1104,18 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
goto err_hif_stop;
}
/* If firmware indicates Full Rx Reorder support it must be used in a
* slightly different manner. Let HTT code know.
*/
ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
ar->wmi.svc_map));
status = ath10k_htt_rx_ring_refill(ar);
if (status) {
ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
goto err_hif_stop;
}
/* we don't care about HTT in UTF mode */
if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_htt_setup(&ar->htt);
@ -1295,6 +1351,7 @@ EXPORT_SYMBOL(ath10k_core_unregister);
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
enum ath10k_bus bus,
enum ath10k_hw_rev hw_rev,
const struct ath10k_hif_ops *hif_ops)
{
struct ath10k *ar;
@ -1307,9 +1364,24 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
ar->ath_common.priv = ar;
ar->ath_common.hw = ar->hw;
ar->dev = dev;
ar->hw_rev = hw_rev;
ar->hif.ops = hif_ops;
ar->hif.bus = bus;
switch (hw_rev) {
case ATH10K_HW_QCA988X:
ar->regs = &qca988x_regs;
break;
case ATH10K_HW_QCA6174:
ar->regs = &qca6174_regs;
break;
default:
ath10k_err(ar, "unsupported core hardware revision %d\n",
hw_rev);
ret = -ENOTSUPP;
goto err_free_mac;
}
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
init_completion(&ar->scan.on_channel);

View file

@ -97,6 +97,11 @@ struct ath10k_skb_cb {
} bcn;
} __packed;
struct ath10k_skb_rxcb {
dma_addr_t paddr;
struct hlist_node hlist;
};
static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
@ -104,6 +109,15 @@ static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
}
static inline struct ath10k_skb_rxcb *ATH10K_SKB_RXCB(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(struct ath10k_skb_rxcb) > sizeof(skb->cb));
return (struct ath10k_skb_rxcb *)skb->cb;
}
#define ATH10K_RXCB_SKB(rxcb) \
container_of((void *)rxcb, struct sk_buff, cb)
static inline u32 host_interest_item_address(u32 item_offset)
{
return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
@ -239,10 +253,21 @@ struct ath10k_sta {
u32 smps;
struct work_struct update_wk;
#ifdef CONFIG_MAC80211_DEBUGFS
/* protected by conf_mutex */
bool aggr_mode;
#endif
};
#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
enum ath10k_beacon_state {
ATH10K_BEACON_SCHEDULED = 0,
ATH10K_BEACON_SENDING,
ATH10K_BEACON_SENT,
};
struct ath10k_vif {
struct list_head list;
@ -253,7 +278,7 @@ struct ath10k_vif {
u32 dtim_period;
struct sk_buff *beacon;
/* protected by data_lock */
bool beacon_sent;
enum ath10k_beacon_state beacon_state;
void *beacon_buf;
dma_addr_t beacon_paddr;
@ -266,10 +291,8 @@ struct ath10k_vif {
u32 aid;
u8 bssid[ETH_ALEN];
struct work_struct wep_key_work;
struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
u8 def_wep_key_idx;
u8 def_wep_key_newidx;
s8 def_wep_key_idx;
u16 tx_seq_no;
@ -296,6 +319,7 @@ struct ath10k_vif {
bool use_cts_prot;
int num_legacy_stations;
int txpower;
struct wmi_wmm_params_all_arg wmm_params;
};
struct ath10k_vif_iter {
@ -326,6 +350,7 @@ struct ath10k_debug {
/* protected by conf_mutex */
u32 fw_dbglog_mask;
u32 fw_dbglog_level;
u32 pktlog_filter;
u32 reg_addr;
u32 nf_cal_period;
@ -452,6 +477,7 @@ struct ath10k {
struct device *dev;
u8 mac_addr[ETH_ALEN];
enum ath10k_hw_rev hw_rev;
u32 chip_id;
u32 target_version;
u8 fw_version_major;
@ -467,9 +493,6 @@ struct ath10k {
DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
struct targetdef *targetdef;
struct hostdef *hostdef;
bool p2p;
struct {
@ -479,6 +502,7 @@ struct ath10k {
struct completion target_suspend;
const struct ath10k_hw_regs *regs;
struct ath10k_bmi bmi;
struct ath10k_wmi wmi;
struct ath10k_htc htc;
@ -559,7 +583,6 @@ struct ath10k {
u8 cfg_tx_chainmask;
u8 cfg_rx_chainmask;
struct wmi_pdev_set_wmm_params_arg wmm_params;
struct completion install_key_done;
struct completion vdev_setup_done;
@ -643,6 +666,7 @@ struct ath10k {
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
enum ath10k_bus bus,
enum ath10k_hw_rev hw_rev,
const struct ath10k_hif_ops *hif_ops);
void ath10k_core_destroy(struct ath10k *ar);

View file

@ -371,7 +371,7 @@ static int ath10k_debug_fw_stats_request(struct ath10k *ar)
ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
1*HZ);
if (ret <= 0)
if (ret == 0)
return -ETIMEDOUT;
spin_lock_bh(&ar->data_lock);
@ -1318,10 +1318,10 @@ static ssize_t ath10k_read_fw_dbglog(struct file *file,
{
struct ath10k *ar = file->private_data;
unsigned int len;
char buf[32];
char buf[64];
len = scnprintf(buf, sizeof(buf), "0x%08x\n",
ar->debug.fw_dbglog_mask);
len = scnprintf(buf, sizeof(buf), "0x%08x %u\n",
ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@ -1331,19 +1331,32 @@ static ssize_t ath10k_write_fw_dbglog(struct file *file,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
unsigned long mask;
int ret;
char buf[64];
unsigned int log_level, mask;
ret = kstrtoul_from_user(user_buf, count, 0, &mask);
if (ret)
return ret;
simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
/* make sure that buf is null terminated */
buf[sizeof(buf) - 1] = 0;
ret = sscanf(buf, "%x %u", &mask, &log_level);
if (!ret)
return -EINVAL;
if (ret == 1)
/* default if user did not specify */
log_level = ATH10K_DBGLOG_LEVEL_WARN;
mutex_lock(&ar->conf_mutex);
ar->debug.fw_dbglog_mask = mask;
ar->debug.fw_dbglog_level = log_level;
if (ar->state == ATH10K_STATE_ON) {
ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
ar->debug.fw_dbglog_level);
if (ret) {
ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",
ret);
@ -1685,7 +1698,8 @@ int ath10k_debug_start(struct ath10k *ar)
ret);
if (ar->debug.fw_dbglog_mask) {
ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
ATH10K_DBGLOG_LEVEL_WARN);
if (ret)
/* not serious */
ath10k_warn(ar, "failed to enable dbglog during start: %d",

View file

@ -48,6 +48,12 @@ enum ath10k_pktlog_filter {
ATH10K_PKTLOG_ANY = 0x00000001f,
};
enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_AUTO,
ATH10K_DBG_AGGR_MODE_MANUAL,
ATH10K_DBG_AGGR_MODE_MAX,
};
extern unsigned int ath10k_debug_mask;
__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
@ -77,7 +83,6 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ethtool_stats *stats, u64 *data);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@ -129,6 +134,10 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
#define ath10k_debug_get_et_stats NULL
#endif /* CONFIG_ATH10K_DEBUGFS */
#ifdef CONFIG_MAC80211_DEBUGFS
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
#endif /* CONFIG_MAC80211_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
__printf(3, 4) void ath10k_dbg(struct ath10k *ar,

View file

@ -0,0 +1,243 @@
/*
* 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 "core.h"
#include "wmi-ops.h"
#include "debug.h"
static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
char buf[32];
int len = 0;
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n",
(arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ?
"auto" : "manual");
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
u32 aggr_mode;
int ret;
if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode))
return -EINVAL;
if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(aggr_mode == arsta->aggr_mode)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr);
if (ret) {
ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret);
goto out;
}
arsta->aggr_mode = aggr_mode;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_aggr_mode = {
.read = ath10k_dbg_sta_read_aggr_mode,
.write = ath10k_dbg_sta_write_aggr_mode,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_addba(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, buf_size;
int ret;
char buf[64];
simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
/* make sure that buf is null terminated */
buf[sizeof(buf) - 1] = '\0';
ret = sscanf(buf, "%u %u", &tid, &buf_size);
if (ret != 2)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr,
tid, buf_size);
if (ret) {
ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n",
arsta->arvif->vdev_id, sta->addr, tid, buf_size);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_addba = {
.write = ath10k_dbg_sta_write_addba,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, status;
int ret;
char buf[64];
simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
/* make sure that buf is null terminated */
buf[sizeof(buf) - 1] = '\0';
ret = sscanf(buf, "%u %u", &tid, &status);
if (ret != 2)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr,
tid, status);
if (ret) {
ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n",
arsta->arvif->vdev_id, sta->addr, tid, status);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_addba_resp = {
.write = ath10k_dbg_sta_write_addba_resp,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_delba(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, initiator, reason;
int ret;
char buf[64];
simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
/* make sure that buf is null terminated */
buf[sizeof(buf) - 1] = '\0';
ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason);
if (ret != 3)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr,
tid, initiator, reason);
if (ret) {
ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n",
arsta->arvif->vdev_id, sta->addr, tid, initiator,
reason);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_delba = {
.write = ath10k_dbg_sta_write_delba,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir)
{
debugfs_create_file("aggr_mode", S_IRUGO | S_IWUSR, dir, sta,
&fops_aggr_mode);
debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba);
debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp);
debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba);
}

View file

@ -703,11 +703,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
/* wait for response */
status = wait_for_completion_timeout(&htc->ctl_resp,
ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
if (status <= 0) {
if (status == 0)
status = -ETIMEDOUT;
if (status == 0) {
ath10k_err(ar, "Service connect timeout: %d\n", status);
return status;
return -ETIMEDOUT;
}
/* we controlled the buffer creation, it's aligned */

View file

@ -53,7 +53,6 @@ int ath10k_htt_init(struct ath10k *ar)
struct ath10k_htt *htt = &ar->htt;
htt->ar = ar;
htt->max_throughput_mbps = 800;
/*
* Prefetch enough data to satisfy target
@ -102,7 +101,7 @@ int ath10k_htt_setup(struct ath10k_htt *htt)
status = wait_for_completion_timeout(&htt->target_version_received,
HTT_TARGET_VERSION_TIMEOUT_HZ);
if (status <= 0) {
if (status == 0) {
ath10k_warn(ar, "htt version request timed out\n");
return -ETIMEDOUT;
}

View file

@ -21,6 +21,7 @@
#include <linux/bug.h>
#include <linux/interrupt.h>
#include <linux/dmapool.h>
#include <linux/hashtable.h>
#include <net/mac80211.h>
#include "htc.h"
@ -286,7 +287,19 @@ enum htt_t2h_msg_type {
HTT_T2H_MSG_TYPE_RC_UPDATE_IND = 0xc,
HTT_T2H_MSG_TYPE_TX_INSPECT_IND = 0xd,
HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION = 0xe,
HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND = 0xf,
HTT_T2H_MSG_TYPE_RX_PN_IND = 0x10,
HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND = 0x11,
HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND = 0x12,
/* 0x13 reservd */
HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE = 0x14,
/* FIXME: Do not depend on this event id. Numbering of this event id is
* broken across different firmware revisions and HTT version fails to
* indicate this.
*/
HTT_T2H_MSG_TYPE_TEST,
/* keep this last */
HTT_T2H_NUM_MSGS
};
@ -655,6 +668,53 @@ struct htt_rx_fragment_indication {
#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK 0x00000FC0
#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB 6
struct htt_rx_pn_ind {
__le16 peer_id;
u8 tid;
u8 seqno_start;
u8 seqno_end;
u8 pn_ie_count;
u8 reserved;
u8 pn_ies[0];
} __packed;
struct htt_rx_offload_msdu {
__le16 msdu_len;
__le16 peer_id;
u8 vdev_id;
u8 tid;
u8 fw_desc;
u8 payload[0];
} __packed;
struct htt_rx_offload_ind {
u8 reserved;
__le16 msdu_count;
} __packed;
struct htt_rx_in_ord_msdu_desc {
__le32 msdu_paddr;
__le16 msdu_len;
u8 fw_desc;
u8 reserved;
} __packed;
struct htt_rx_in_ord_ind {
u8 info;
__le16 peer_id;
u8 vdev_id;
u8 reserved;
__le16 msdu_count;
struct htt_rx_in_ord_msdu_desc msdu_descs[0];
} __packed;
#define HTT_RX_IN_ORD_IND_INFO_TID_MASK 0x0000001f
#define HTT_RX_IN_ORD_IND_INFO_TID_LSB 0
#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK 0x00000020
#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_LSB 5
#define HTT_RX_IN_ORD_IND_INFO_FRAG_MASK 0x00000040
#define HTT_RX_IN_ORD_IND_INFO_FRAG_LSB 6
/*
* target -> host test message definition
*
@ -1150,6 +1210,9 @@ struct htt_resp {
struct htt_rx_test rx_test;
struct htt_pktlog_msg pktlog_msg;
struct htt_stats_conf stats_conf;
struct htt_rx_pn_ind rx_pn_ind;
struct htt_rx_offload_ind rx_offload_ind;
struct htt_rx_in_ord_ind rx_in_ord_ind;
};
} __packed;
@ -1182,7 +1245,6 @@ struct ath10k_htt {
struct ath10k *ar;
enum ath10k_htc_ep_id eid;
int max_throughput_mbps;
u8 target_version_major;
u8 target_version_minor;
struct completion target_version_received;
@ -1198,6 +1260,20 @@ struct ath10k_htt {
* filled.
*/
struct sk_buff **netbufs_ring;
/* This is used only with firmware supporting IN_ORD_IND.
*
* With Full Rx Reorder the HTT Rx Ring is more of a temporary
* buffer ring from which buffer addresses are copied by the
* firmware to MAC Rx ring. Firmware then delivers IN_ORD_IND
* pointing to specific (re-ordered) buffers.
*
* FIXME: With kernel generic hashing functions there's a lot
* of hash collisions for sk_buffs.
*/
bool in_ord_rx;
DECLARE_HASHTABLE(skb_table, 4);
/*
* Ring of buffer addresses -
* This ring holds the "physical" device address of the
@ -1252,12 +1328,11 @@ struct ath10k_htt {
unsigned int prefetch_len;
/* Protects access to %pending_tx, %used_msdu_ids */
/* Protects access to pending_tx, num_pending_tx */
spinlock_t tx_lock;
int max_num_pending_tx;
int num_pending_tx;
struct sk_buff **pending_tx;
unsigned long *used_msdu_ids; /* bitmap */
struct idr pending_tx;
wait_queue_head_t empty_tx_wq;
struct dma_pool *tx_pool;
@ -1271,6 +1346,7 @@ struct ath10k_htt {
struct tasklet_struct txrx_compl_task;
struct sk_buff_head tx_compl_q;
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_in_ord_compl_q;
/* rx_status template */
struct ieee80211_rx_status rx_status;
@ -1334,6 +1410,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt);
void ath10k_htt_tx_free(struct ath10k_htt *htt);
int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
int ath10k_htt_rx_ring_refill(struct ath10k *ar);
void ath10k_htt_rx_free(struct ath10k_htt *htt);
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
@ -1346,7 +1423,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_amsdu);
void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);

View file

@ -25,8 +25,8 @@
#include <linux/log2.h>
#define HTT_RX_RING_SIZE 1024
#define HTT_RX_RING_FILL_LEVEL 1000
#define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
#define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
@ -34,31 +34,70 @@
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static void ath10k_htt_txrx_compl_task(unsigned long ptr);
static struct sk_buff *
ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u32 paddr)
{
struct ath10k_skb_rxcb *rxcb;
hash_for_each_possible(ar->htt.rx_ring.skb_table, rxcb, hlist, paddr)
if (rxcb->paddr == paddr)
return ATH10K_RXCB_SKB(rxcb);
WARN_ON_ONCE(1);
return NULL;
}
static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
{
struct sk_buff *skb;
struct ath10k_skb_cb *cb;
struct ath10k_skb_rxcb *rxcb;
struct hlist_node *n;
int i;
for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
skb = htt->rx_ring.netbufs_ring[i];
cb = ATH10K_SKB_CB(skb);
dma_unmap_single(htt->ar->dev, cb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
if (htt->rx_ring.in_ord_rx) {
hash_for_each_safe(htt->rx_ring.skb_table, i, n, rxcb, hlist) {
skb = ATH10K_RXCB_SKB(rxcb);
dma_unmap_single(htt->ar->dev, rxcb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
hash_del(&rxcb->hlist);
dev_kfree_skb_any(skb);
}
} else {
for (i = 0; i < htt->rx_ring.size; i++) {
skb = htt->rx_ring.netbufs_ring[i];
if (!skb)
continue;
rxcb = ATH10K_SKB_RXCB(skb);
dma_unmap_single(htt->ar->dev, rxcb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
}
htt->rx_ring.fill_cnt = 0;
hash_init(htt->rx_ring.skb_table);
memset(htt->rx_ring.netbufs_ring, 0,
htt->rx_ring.size * sizeof(htt->rx_ring.netbufs_ring[0]));
}
static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
{
struct htt_rx_desc *rx_desc;
struct ath10k_skb_rxcb *rxcb;
struct sk_buff *skb;
dma_addr_t paddr;
int ret = 0, idx;
/* The Full Rx Reorder firmware has no way of telling the host
* implicitly when it copied HTT Rx Ring buffers to MAC Rx Ring.
* To keep things simple make sure ring is always half empty. This
* guarantees there'll be no replenishment overruns possible.
*/
BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2);
idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
while (num > 0) {
skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
@ -86,17 +125,29 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
goto fail;
}
ATH10K_SKB_CB(skb)->paddr = paddr;
rxcb = ATH10K_SKB_RXCB(skb);
rxcb->paddr = paddr;
htt->rx_ring.netbufs_ring[idx] = skb;
htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
htt->rx_ring.fill_cnt++;
if (htt->rx_ring.in_ord_rx) {
hash_add(htt->rx_ring.skb_table,
&ATH10K_SKB_RXCB(skb)->hlist,
(u32)paddr);
}
num--;
idx++;
idx &= htt->rx_ring.size_mask;
}
fail:
/*
* Make sure the rx buffer is updated before available buffer
* index to avoid any potential rx ring corruption.
*/
mb();
*htt->rx_ring.alloc_idx.vaddr = __cpu_to_le32(idx);
return ret;
}
@ -153,22 +204,20 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
ath10k_htt_rx_msdu_buff_replenish(htt);
}
static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt)
int ath10k_htt_rx_ring_refill(struct ath10k *ar)
{
struct sk_buff *skb;
int i;
struct ath10k_htt *htt = &ar->htt;
int ret;
for (i = 0; i < htt->rx_ring.size; i++) {
skb = htt->rx_ring.netbufs_ring[i];
if (!skb)
continue;
spin_lock_bh(&htt->rx_ring.lock);
ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level -
htt->rx_ring.fill_cnt));
spin_unlock_bh(&htt->rx_ring.lock);
dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
htt->rx_ring.netbufs_ring[i] = NULL;
}
if (ret)
ath10k_htt_rx_ring_free(htt);
return ret;
}
void ath10k_htt_rx_free(struct ath10k_htt *htt)
@ -179,8 +228,9 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
skb_queue_purge(&htt->tx_compl_q);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
ath10k_htt_rx_ring_clean_up(htt);
ath10k_htt_rx_ring_free(htt);
dma_free_coherent(htt->ar->dev,
(htt->rx_ring.size *
@ -212,6 +262,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
idx = htt->rx_ring.sw_rd_idx.msdu_payld;
msdu = htt->rx_ring.netbufs_ring[idx];
htt->rx_ring.netbufs_ring[idx] = NULL;
htt->rx_ring.paddrs_ring[idx] = 0;
idx++;
idx &= htt->rx_ring.size_mask;
@ -219,7 +270,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
htt->rx_ring.fill_cnt--;
dma_unmap_single(htt->ar->dev,
ATH10K_SKB_CB(msdu)->paddr,
ATH10K_SKB_RXCB(msdu)->paddr,
msdu->len + skb_tailroom(msdu),
DMA_FROM_DEVICE);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
@ -379,6 +430,82 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr)
ath10k_htt_rx_msdu_buff_replenish(htt);
}
static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
u32 paddr)
{
struct ath10k *ar = htt->ar;
struct ath10k_skb_rxcb *rxcb;
struct sk_buff *msdu;
lockdep_assert_held(&htt->rx_ring.lock);
msdu = ath10k_htt_rx_find_skb_paddr(ar, paddr);
if (!msdu)
return NULL;
rxcb = ATH10K_SKB_RXCB(msdu);
hash_del(&rxcb->hlist);
htt->rx_ring.fill_cnt--;
dma_unmap_single(htt->ar->dev, rxcb->paddr,
msdu->len + skb_tailroom(msdu),
DMA_FROM_DEVICE);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
msdu->data, msdu->len + skb_tailroom(msdu));
return msdu;
}
static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt,
struct htt_rx_in_ord_ind *ev,
struct sk_buff_head *list)
{
struct ath10k *ar = htt->ar;
struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs;
struct htt_rx_desc *rxd;
struct sk_buff *msdu;
int msdu_count;
bool is_offload;
u32 paddr;
lockdep_assert_held(&htt->rx_ring.lock);
msdu_count = __le16_to_cpu(ev->msdu_count);
is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
while (msdu_count--) {
paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
msdu = ath10k_htt_rx_pop_paddr(htt, paddr);
if (!msdu) {
__skb_queue_purge(list);
return -ENOENT;
}
__skb_queue_tail(list, msdu);
if (!is_offload) {
rxd = (void *)msdu->data;
trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));
skb_put(msdu, sizeof(*rxd));
skb_pull(msdu, sizeof(*rxd));
skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len));
if (!(__le32_to_cpu(rxd->attention.flags) &
RX_ATTENTION_FLAGS_MSDU_DONE)) {
ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
return -EIO;
}
}
msdu_desc++;
}
return 0;
}
int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
@ -424,7 +551,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
htt->rx_ring.alloc_idx.vaddr = vaddr;
htt->rx_ring.alloc_idx.paddr = paddr;
htt->rx_ring.sw_rd_idx.msdu_payld = 0;
htt->rx_ring.sw_rd_idx.msdu_payld = htt->rx_ring.size_mask;
*htt->rx_ring.alloc_idx.vaddr = 0;
/* Initialize the Rx refill retry timer */
@ -433,14 +560,15 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
spin_lock_init(&htt->rx_ring.lock);
htt->rx_ring.fill_cnt = 0;
if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
goto err_fill_ring;
htt->rx_ring.sw_rd_idx.msdu_payld = 0;
hash_init(htt->rx_ring.skb_table);
tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
(unsigned long)htt);
skb_queue_head_init(&htt->tx_compl_q);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
(unsigned long)htt);
@ -449,12 +577,6 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
htt->rx_ring.size, htt->rx_ring.fill_level);
return 0;
err_fill_ring:
ath10k_htt_rx_ring_free(htt);
dma_free_coherent(htt->ar->dev,
sizeof(*htt->rx_ring.alloc_idx.vaddr),
htt->rx_ring.alloc_idx.vaddr,
htt->rx_ring.alloc_idx.paddr);
err_dma_idx:
dma_free_coherent(htt->ar->dev,
(htt->rx_ring.size *
@ -691,7 +813,7 @@ static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
*
* FIXME: Can we get/compute 64bit TSF?
*/
status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp);
status->flag |= RX_FLAG_MACTIME_END;
}
@ -1578,6 +1700,194 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp)
spin_unlock_bh(&ar->data_lock);
}
static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list,
struct sk_buff_head *amsdu)
{
struct sk_buff *msdu;
struct htt_rx_desc *rxd;
if (skb_queue_empty(list))
return -ENOBUFS;
if (WARN_ON(!skb_queue_empty(amsdu)))
return -EINVAL;
while ((msdu = __skb_dequeue(list))) {
__skb_queue_tail(amsdu, msdu);
rxd = (void *)msdu->data - sizeof(*rxd);
if (rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))
break;
}
msdu = skb_peek_tail(amsdu);
rxd = (void *)msdu->data - sizeof(*rxd);
if (!(rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) {
skb_queue_splice_init(amsdu, list);
return -EAGAIN;
}
return 0;
}
static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_has_protected(hdr->frame_control))
return;
/* Offloaded frames are already decrypted but firmware insists they are
* protected in the 802.11 header. Strip the flag. Otherwise mac80211
* will drop the frame.
*/
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
status->flag |= RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
}
static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
struct sk_buff_head *list)
{
struct ath10k_htt *htt = &ar->htt;
struct ieee80211_rx_status *status = &htt->rx_status;
struct htt_rx_offload_msdu *rx;
struct sk_buff *msdu;
size_t offset;
while ((msdu = __skb_dequeue(list))) {
/* Offloaded frames don't have Rx descriptor. Instead they have
* a short meta information header.
*/
rx = (void *)msdu->data;
skb_put(msdu, sizeof(*rx));
skb_pull(msdu, sizeof(*rx));
if (skb_tailroom(msdu) < __le16_to_cpu(rx->msdu_len)) {
ath10k_warn(ar, "dropping frame: offloaded rx msdu is too long!\n");
dev_kfree_skb_any(msdu);
continue;
}
skb_put(msdu, __le16_to_cpu(rx->msdu_len));
/* Offloaded rx header length isn't multiple of 2 nor 4 so the
* actual payload is unaligned. Align the frame. Otherwise
* mac80211 complains. This shouldn't reduce performance much
* because these offloaded frames are rare.
*/
offset = 4 - ((unsigned long)msdu->data & 3);
skb_put(msdu, offset);
memmove(msdu->data + offset, msdu->data, msdu->len);
skb_pull(msdu, offset);
/* FIXME: The frame is NWifi. Re-construct QoS Control
* if possible later.
*/
memset(status, 0, sizeof(*status));
status->flag |= RX_FLAG_NO_SIGNAL_VAL;
ath10k_htt_rx_h_rx_offload_prot(status, msdu);
ath10k_htt_rx_h_channel(ar, status);
ath10k_process_rx(ar, status, msdu);
}
}
static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
struct htt_resp *resp = (void *)skb->data;
struct ieee80211_rx_status *status = &htt->rx_status;
struct sk_buff_head list;
struct sk_buff_head amsdu;
u16 peer_id;
u16 msdu_count;
u8 vdev_id;
u8 tid;
bool offload;
bool frag;
int ret;
lockdep_assert_held(&htt->rx_ring.lock);
if (htt->rx_confused)
return;
skb_pull(skb, sizeof(resp->hdr));
skb_pull(skb, sizeof(resp->rx_in_ord_ind));
peer_id = __le16_to_cpu(resp->rx_in_ord_ind.peer_id);
msdu_count = __le16_to_cpu(resp->rx_in_ord_ind.msdu_count);
vdev_id = resp->rx_in_ord_ind.vdev_id;
tid = SM(resp->rx_in_ord_ind.info, HTT_RX_IN_ORD_IND_INFO_TID);
offload = !!(resp->rx_in_ord_ind.info &
HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
frag = !!(resp->rx_in_ord_ind.info & HTT_RX_IN_ORD_IND_INFO_FRAG_MASK);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt rx in ord vdev %i peer %i tid %i offload %i frag %i msdu count %i\n",
vdev_id, peer_id, tid, offload, frag, msdu_count);
if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs)) {
ath10k_warn(ar, "dropping invalid in order rx indication\n");
return;
}
/* The event can deliver more than 1 A-MSDU. Each A-MSDU is later
* extracted and processed.
*/
__skb_queue_head_init(&list);
ret = ath10k_htt_rx_pop_paddr_list(htt, &resp->rx_in_ord_ind, &list);
if (ret < 0) {
ath10k_warn(ar, "failed to pop paddr list: %d\n", ret);
htt->rx_confused = true;
return;
}
/* Offloaded frames are very different and need to be handled
* separately.
*/
if (offload)
ath10k_htt_rx_h_rx_offload(ar, &list);
while (!skb_queue_empty(&list)) {
__skb_queue_head_init(&amsdu);
ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu);
switch (ret) {
case 0:
/* Note: The in-order indication may report interleaved
* frames from different PPDUs meaning reported rx rate
* to mac80211 isn't accurate/reliable. It's still
* better to report something than nothing though. This
* should still give an idea about rx rate to the user.
*/
ath10k_htt_rx_h_ppdu(ar, &amsdu, status);
ath10k_htt_rx_h_filter(ar, &amsdu, status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
ath10k_htt_rx_h_deliver(ar, &amsdu, status);
break;
case -EAGAIN:
/* fall through */
default:
/* Should not happen. */
ath10k_warn(ar, "failed to extract amsdu: %d\n", ret);
htt->rx_confused = true;
__skb_queue_purge(&list);
return;
}
}
tasklet_schedule(&htt->rx_replenish_task);
}
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@ -1700,6 +2010,20 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
*/
break;
}
case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
spin_lock_bh(&htt->rx_ring.lock);
__skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
spin_unlock_bh(&htt->rx_ring.lock);
tasklet_schedule(&htt->txrx_compl_task);
return;
}
case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
/* FIXME: This WMI-TLV event is overlapping with 10.2
* CHAN_CHANGE - both being 0xF. Neither is being used in
* practice so no immediate action is necessary. Nevertheless
* HTT may need an abstraction layer like WMI has one day.
*/
break;
default:
ath10k_warn(ar, "htt event (%d) not handled\n",
resp->hdr.msg_type);
@ -1715,6 +2039,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
static void ath10k_htt_txrx_compl_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
struct ath10k *ar = htt->ar;
struct htt_resp *resp;
struct sk_buff *skb;
@ -1731,5 +2056,10 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
ath10k_htt_rx_handler(htt, &resp->rx_ind);
dev_kfree_skb_any(skb);
}
while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
ath10k_htt_rx_in_ord_ind(ar, skb);
dev_kfree_skb_any(skb);
}
spin_unlock_bh(&htt->rx_ring.lock);
}

View file

@ -56,21 +56,18 @@ exit:
return ret;
}
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
{
struct ath10k *ar = htt->ar;
int msdu_id;
int ret;
lockdep_assert_held(&htt->tx_lock);
msdu_id = find_first_zero_bit(htt->used_msdu_ids,
htt->max_num_pending_tx);
if (msdu_id == htt->max_num_pending_tx)
return -ENOBUFS;
ret = idr_alloc(&htt->pending_tx, skb, 0, 0x10000, GFP_ATOMIC);
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
__set_bit(msdu_id, htt->used_msdu_ids);
return msdu_id;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", ret);
return ret;
}
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
@ -79,74 +76,53 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
lockdep_assert_held(&htt->tx_lock);
if (!test_bit(msdu_id, htt->used_msdu_ids))
ath10k_warn(ar, "trying to free unallocated msdu_id %d\n",
msdu_id);
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
__clear_bit(msdu_id, htt->used_msdu_ids);
idr_remove(&htt->pending_tx, msdu_id);
}
int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
spin_lock_init(&htt->tx_lock);
ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
htt->max_num_pending_tx);
htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
htt->max_num_pending_tx, GFP_KERNEL);
if (!htt->pending_tx)
return -ENOMEM;
htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
BITS_TO_LONGS(htt->max_num_pending_tx),
GFP_KERNEL);
if (!htt->used_msdu_ids) {
kfree(htt->pending_tx);
return -ENOMEM;
}
spin_lock_init(&htt->tx_lock);
idr_init(&htt->pending_tx);
htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
sizeof(struct ath10k_htt_txbuf), 4, 0);
if (!htt->tx_pool) {
kfree(htt->used_msdu_ids);
kfree(htt->pending_tx);
idr_destroy(&htt->pending_tx);
return -ENOMEM;
}
return 0;
}
static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt)
static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
{
struct ath10k *ar = htt->ar;
struct ath10k *ar = ctx;
struct ath10k_htt *htt = &ar->htt;
struct htt_tx_done tx_done = {0};
int msdu_id;
ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id);
tx_done.discard = 1;
tx_done.msdu_id = msdu_id;
spin_lock_bh(&htt->tx_lock);
for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
if (!test_bit(msdu_id, htt->used_msdu_ids))
continue;
ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
msdu_id);
tx_done.discard = 1;
tx_done.msdu_id = msdu_id;
ath10k_txrx_tx_unref(htt, &tx_done);
}
ath10k_txrx_tx_unref(htt, &tx_done);
spin_unlock_bh(&htt->tx_lock);
return 0;
}
void ath10k_htt_tx_free(struct ath10k_htt *htt)
{
ath10k_htt_tx_free_pending(htt);
kfree(htt->pending_tx);
kfree(htt->used_msdu_ids);
idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
idr_destroy(&htt->pending_tx);
dma_pool_destroy(htt->tx_pool);
}
@ -378,13 +354,12 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
len += sizeof(cmd->mgmt_tx);
spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
if (res < 0) {
spin_unlock_bh(&htt->tx_lock);
goto err_tx_dec;
}
msdu_id = res;
htt->pending_tx[msdu_id] = msdu;
spin_unlock_bh(&htt->tx_lock);
txdesc = ath10k_htc_alloc_skb(ar, len);
@ -423,7 +398,6 @@ err_free_txdesc:
dev_kfree_skb_any(txdesc);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:
@ -455,13 +429,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
goto err;
spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
if (res < 0) {
spin_unlock_bh(&htt->tx_lock);
goto err_tx_dec;
}
msdu_id = res;
htt->pending_tx[msdu_id] = msdu;
spin_unlock_bh(&htt->tx_lock);
prefetch_len = min(htt->prefetch_len, msdu->len);
@ -475,10 +448,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
&paddr);
if (!skb_cb->htt.txbuf)
if (!skb_cb->htt.txbuf) {
res = -ENOMEM;
goto err_free_msdu_id;
}
skb_cb->htt.txbuf_paddr = paddr;
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control))
skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
DMA_TO_DEVICE);
res = dma_mapping_error(dev, skb_cb->paddr);
@ -534,8 +515,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
if (msdu->ip_summed == CHECKSUM_PARTIAL) {
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
}
/* Prevent firmware from sending up tx inspection requests. There's
* nothing ath10k can do with frames requested for inspection so force
@ -593,7 +576,6 @@ err_free_txbuf:
skb_cb->htt.txbuf_paddr);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2014-2015 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/types.h>
#include "hw.h"
const struct ath10k_hw_regs qca988x_regs = {
.rtc_state_cold_reset_mask = 0x00000400,
.rtc_soc_base_address = 0x00004000,
.rtc_wmac_base_address = 0x00005000,
.soc_core_base_address = 0x00009000,
.ce_wrapper_base_address = 0x00057000,
.ce0_base_address = 0x00057400,
.ce1_base_address = 0x00057800,
.ce2_base_address = 0x00057c00,
.ce3_base_address = 0x00058000,
.ce4_base_address = 0x00058400,
.ce5_base_address = 0x00058800,
.ce6_base_address = 0x00058c00,
.ce7_base_address = 0x00059000,
.soc_reset_control_si0_rst_mask = 0x00000001,
.soc_reset_control_ce_rst_mask = 0x00040000,
.soc_chip_id_address = 0x00ec,
.scratch_3_address = 0x0030,
};
const struct ath10k_hw_regs qca6174_regs = {
.rtc_state_cold_reset_mask = 0x00002000,
.rtc_soc_base_address = 0x00000800,
.rtc_wmac_base_address = 0x00001000,
.soc_core_base_address = 0x0003a000,
.ce_wrapper_base_address = 0x00034000,
.ce0_base_address = 0x00034400,
.ce1_base_address = 0x00034800,
.ce2_base_address = 0x00034c00,
.ce3_base_address = 0x00035000,
.ce4_base_address = 0x00035400,
.ce5_base_address = 0x00035800,
.ce6_base_address = 0x00035c00,
.ce7_base_address = 0x00036000,
.soc_reset_control_si0_rst_mask = 0x00000000,
.soc_reset_control_ce_rst_mask = 0x00000001,
.soc_chip_id_address = 0x000f0,
.scratch_3_address = 0x0028,
};

View file

@ -34,6 +34,44 @@
#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
/* QCA6174 target BMI version signatures */
#define QCA6174_HW_1_0_VERSION 0x05000000
#define QCA6174_HW_1_1_VERSION 0x05000001
#define QCA6174_HW_1_3_VERSION 0x05000003
#define QCA6174_HW_2_1_VERSION 0x05010000
#define QCA6174_HW_3_0_VERSION 0x05020000
#define QCA6174_HW_3_2_VERSION 0x05030000
enum qca6174_pci_rev {
QCA6174_PCI_REV_1_1 = 0x11,
QCA6174_PCI_REV_1_3 = 0x13,
QCA6174_PCI_REV_2_0 = 0x20,
QCA6174_PCI_REV_3_0 = 0x30,
};
enum qca6174_chip_id_rev {
QCA6174_HW_1_0_CHIP_ID_REV = 0,
QCA6174_HW_1_1_CHIP_ID_REV = 1,
QCA6174_HW_1_3_CHIP_ID_REV = 2,
QCA6174_HW_2_1_CHIP_ID_REV = 4,
QCA6174_HW_2_2_CHIP_ID_REV = 5,
QCA6174_HW_3_0_CHIP_ID_REV = 8,
QCA6174_HW_3_1_CHIP_ID_REV = 9,
QCA6174_HW_3_2_CHIP_ID_REV = 10,
};
#define QCA6174_HW_2_1_FW_DIR "ath10k/QCA6174/hw2.1"
#define QCA6174_HW_2_1_FW_FILE "firmware.bin"
#define QCA6174_HW_2_1_OTP_FILE "otp.bin"
#define QCA6174_HW_2_1_BOARD_DATA_FILE "board.bin"
#define QCA6174_HW_2_1_PATCH_LOAD_ADDR 0x1234
#define QCA6174_HW_3_0_FW_DIR "ath10k/QCA6174/hw3.0"
#define QCA6174_HW_3_0_FW_FILE "firmware.bin"
#define QCA6174_HW_3_0_OTP_FILE "otp.bin"
#define QCA6174_HW_3_0_BOARD_DATA_FILE "board.bin"
#define QCA6174_HW_3_0_PATCH_LOAD_ADDR 0x1234
#define ATH10K_FW_API2_FILE "firmware-2.bin"
#define ATH10K_FW_API3_FILE "firmware-3.bin"
@ -81,6 +119,37 @@ enum ath10k_fw_wmi_op_version {
ATH10K_FW_WMI_OP_VERSION_MAX,
};
enum ath10k_hw_rev {
ATH10K_HW_QCA988X,
ATH10K_HW_QCA6174,
};
struct ath10k_hw_regs {
u32 rtc_state_cold_reset_mask;
u32 rtc_soc_base_address;
u32 rtc_wmac_base_address;
u32 soc_core_base_address;
u32 ce_wrapper_base_address;
u32 ce0_base_address;
u32 ce1_base_address;
u32 ce2_base_address;
u32 ce3_base_address;
u32 ce4_base_address;
u32 ce5_base_address;
u32 ce6_base_address;
u32 ce7_base_address;
u32 soc_reset_control_si0_rst_mask;
u32 soc_reset_control_ce_rst_mask;
u32 soc_chip_id_address;
u32 scratch_3_address;
};
extern const struct ath10k_hw_regs qca988x_regs;
extern const struct ath10k_hw_regs qca6174_regs;
#define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X)
#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
/* Known pecularities:
* - current FW doesn't support raw rx mode (last tested v599)
* - current FW dumps upon raw tx mode (last tested v599)
@ -183,6 +252,9 @@ struct ath10k_pktlog_hdr {
#define TARGET_10X_NUM_MSDU_DESC (1024 + 400)
#define TARGET_10X_MAX_FRAG_ENTRIES 0
/* 10.2 parameters */
#define TARGET_10_2_DMA_BURST_SIZE 1
/* Target specific defines for WMI-TLV firmware */
#define TARGET_TLV_NUM_VDEVS 3
#define TARGET_TLV_NUM_STATIONS 32
@ -222,7 +294,7 @@ struct ath10k_pktlog_hdr {
/* as of IP3.7.1 */
#define RTC_STATE_V_ON 3
#define RTC_STATE_COLD_RESET_MASK 0x00000400
#define RTC_STATE_COLD_RESET_MASK ar->regs->rtc_state_cold_reset_mask
#define RTC_STATE_V_LSB 0
#define RTC_STATE_V_MASK 0x00000007
#define RTC_STATE_ADDRESS 0x0000
@ -231,12 +303,12 @@ struct ath10k_pktlog_hdr {
#define PCIE_SOC_WAKE_RESET 0x00000000
#define SOC_GLOBAL_RESET_ADDRESS 0x0008
#define RTC_SOC_BASE_ADDRESS 0x00004000
#define RTC_WMAC_BASE_ADDRESS 0x00005000
#define RTC_SOC_BASE_ADDRESS ar->regs->rtc_soc_base_address
#define RTC_WMAC_BASE_ADDRESS ar->regs->rtc_wmac_base_address
#define MAC_COEX_BASE_ADDRESS 0x00006000
#define BT_COEX_BASE_ADDRESS 0x00007000
#define SOC_PCIE_BASE_ADDRESS 0x00008000
#define SOC_CORE_BASE_ADDRESS 0x00009000
#define SOC_CORE_BASE_ADDRESS ar->regs->soc_core_base_address
#define WLAN_UART_BASE_ADDRESS 0x0000c000
#define WLAN_SI_BASE_ADDRESS 0x00010000
#define WLAN_GPIO_BASE_ADDRESS 0x00014000
@ -245,23 +317,23 @@ struct ath10k_pktlog_hdr {
#define EFUSE_BASE_ADDRESS 0x00030000
#define FPGA_REG_BASE_ADDRESS 0x00039000
#define WLAN_UART2_BASE_ADDRESS 0x00054c00
#define CE_WRAPPER_BASE_ADDRESS 0x00057000
#define CE0_BASE_ADDRESS 0x00057400
#define CE1_BASE_ADDRESS 0x00057800
#define CE2_BASE_ADDRESS 0x00057c00
#define CE3_BASE_ADDRESS 0x00058000
#define CE4_BASE_ADDRESS 0x00058400
#define CE5_BASE_ADDRESS 0x00058800
#define CE6_BASE_ADDRESS 0x00058c00
#define CE7_BASE_ADDRESS 0x00059000
#define CE_WRAPPER_BASE_ADDRESS ar->regs->ce_wrapper_base_address
#define CE0_BASE_ADDRESS ar->regs->ce0_base_address
#define CE1_BASE_ADDRESS ar->regs->ce1_base_address
#define CE2_BASE_ADDRESS ar->regs->ce2_base_address
#define CE3_BASE_ADDRESS ar->regs->ce3_base_address
#define CE4_BASE_ADDRESS ar->regs->ce4_base_address
#define CE5_BASE_ADDRESS ar->regs->ce5_base_address
#define CE6_BASE_ADDRESS ar->regs->ce6_base_address
#define CE7_BASE_ADDRESS ar->regs->ce7_base_address
#define DBI_BASE_ADDRESS 0x00060000
#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000
#define PCIE_LOCAL_BASE_ADDRESS 0x00080000
#define SOC_RESET_CONTROL_ADDRESS 0x00000000
#define SOC_RESET_CONTROL_OFFSET 0x00000000
#define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001
#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000
#define SOC_RESET_CONTROL_SI0_RST_MASK ar->regs->soc_reset_control_si0_rst_mask
#define SOC_RESET_CONTROL_CE_RST_MASK ar->regs->soc_reset_control_ce_rst_mask
#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040
#define SOC_CPU_CLOCK_OFFSET 0x00000020
#define SOC_CPU_CLOCK_STANDARD_LSB 0
@ -275,7 +347,7 @@ struct ath10k_pktlog_hdr {
#define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050
#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004
#define SOC_CHIP_ID_ADDRESS 0x000000ec
#define SOC_CHIP_ID_ADDRESS ar->regs->soc_chip_id_address
#define SOC_CHIP_ID_REV_LSB 8
#define SOC_CHIP_ID_REV_MASK 0x00000f00
@ -331,7 +403,7 @@ struct ath10k_pktlog_hdr {
#define PCIE_INTR_ENABLE_ADDRESS 0x0008
#define PCIE_INTR_CAUSE_ADDRESS 0x000c
#define PCIE_INTR_CLR_ADDRESS 0x0014
#define SCRATCH_3_ADDRESS 0x0030
#define SCRATCH_3_ADDRESS ar->regs->scratch_3_address
#define CPU_INTR_ADDRESS 0x0010
/* Firmware indications to the Host via SCRATCH_3 register. */

View file

@ -37,7 +37,7 @@
static int ath10k_send_key(struct ath10k_vif *arvif,
struct ieee80211_key_conf *key,
enum set_key_cmd cmd,
const u8 *macaddr)
const u8 *macaddr, bool def_idx)
{
struct ath10k *ar = arvif->ar;
struct wmi_vdev_install_key_arg arg = {
@ -58,10 +58,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
arg.key_cipher = WMI_CIPHER_AES_CCM;
if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
else
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
break;
case WLAN_CIPHER_SUITE_TKIP:
arg.key_cipher = WMI_CIPHER_TKIP;
@ -75,6 +72,9 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
* Otherwise pairwise key must be set */
if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
arg.key_flags = WMI_KEY_PAIRWISE;
if (def_idx)
arg.key_flags |= WMI_KEY_TX_USAGE;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
/* this one needs to be done in software */
@ -95,7 +95,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
static int ath10k_install_key(struct ath10k_vif *arvif,
struct ieee80211_key_conf *key,
enum set_key_cmd cmd,
const u8 *macaddr)
const u8 *macaddr, bool def_idx)
{
struct ath10k *ar = arvif->ar;
int ret;
@ -104,7 +104,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
reinit_completion(&ar->install_key_done);
ret = ath10k_send_key(arvif, key, cmd, macaddr);
ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx);
if (ret)
return ret;
@ -122,6 +122,7 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
struct ath10k_peer *peer;
int ret;
int i;
bool def_idx;
lockdep_assert_held(&ar->conf_mutex);
@ -135,9 +136,14 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
if (arvif->wep_keys[i] == NULL)
continue;
/* set TX_USAGE flag for default key id */
if (arvif->def_wep_key_idx == i)
def_idx = true;
else
def_idx = false;
ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
addr);
addr, def_idx);
if (ret)
return ret;
@ -171,8 +177,9 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
if (peer->keys[i] == NULL)
continue;
/* key flags are not required to delete the key */
ret = ath10k_install_key(arvif, peer->keys[i],
DISABLE_KEY, addr);
DISABLE_KEY, addr, false);
if (ret && first_errno == 0)
first_errno = ret;
@ -246,8 +253,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
if (i == ARRAY_SIZE(peer->keys))
break;
ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
/* key flags are not required to delete the key */
ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false);
if (ret && first_errno == 0)
first_errno = ret;
@ -527,10 +534,14 @@ void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
arvif->beacon->len, DMA_TO_DEVICE);
if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED &&
arvif->beacon_state != ATH10K_BEACON_SENT))
return;
dev_kfree_skb_any(arvif->beacon);
arvif->beacon = NULL;
arvif->beacon_sent = false;
arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
}
static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
@ -970,6 +981,143 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
return ret;
}
static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif,
struct sk_buff *bcn)
{
struct ath10k *ar = arvif->ar;
struct ieee80211_mgmt *mgmt;
const u8 *p2p_ie;
int ret;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
return 0;
if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
return 0;
mgmt = (void *)bcn->data;
p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
mgmt->u.beacon.variable,
bcn->len - (mgmt->u.beacon.variable -
bcn->data));
if (!p2p_ie)
return -ENOENT;
ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
if (ret) {
ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
return 0;
}
static int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
u8 oui_type, size_t ie_offset)
{
size_t len;
const u8 *next;
const u8 *end;
u8 *ie;
if (WARN_ON(skb->len < ie_offset))
return -EINVAL;
ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
skb->data + ie_offset,
skb->len - ie_offset);
if (!ie)
return -ENOENT;
len = ie[1] + 2;
end = skb->data + skb->len;
next = ie + len;
if (WARN_ON(next > end))
return -EINVAL;
memmove(ie, next, end - next);
skb_trim(skb, skb->len - len);
return 0;
}
static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_vif *vif = arvif->vif;
struct ieee80211_mutable_offsets offs = {};
struct sk_buff *bcn;
int ret;
if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
return 0;
bcn = ieee80211_beacon_get_template(hw, vif, &offs);
if (!bcn) {
ath10k_warn(ar, "failed to get beacon template from mac80211\n");
return -EPERM;
}
ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn);
if (ret) {
ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
kfree_skb(bcn);
return ret;
}
/* P2P IE is inserted by firmware automatically (as configured above)
* so remove it from the base beacon template to avoid duplicate P2P
* IEs in beacon frames.
*/
ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
offsetof(struct ieee80211_mgmt,
u.beacon.variable));
ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0,
0, NULL, 0);
kfree_skb(bcn);
if (ret) {
ath10k_warn(ar, "failed to submit beacon template command: %d\n",
ret);
return ret;
}
return 0;
}
static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_vif *vif = arvif->vif;
struct sk_buff *prb;
int ret;
if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
return 0;
prb = ieee80211_proberesp_get(hw, vif);
if (!prb) {
ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
return -EPERM;
}
ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb);
kfree_skb(prb);
if (ret) {
ath10k_warn(ar, "failed to submit probe resp template command: %d\n",
ret);
return ret;
}
return 0;
}
static void ath10k_control_beaconing(struct ath10k_vif *arvif,
struct ieee80211_bss_conf *info)
{
@ -1155,6 +1303,38 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
return 0;
}
static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
struct wmi_sta_keepalive_arg arg = {};
int ret;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
return 0;
if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map))
return 0;
/* Some firmware revisions have a bug and ignore the `enabled` field.
* Instead use the interval to disable the keepalive.
*/
arg.vdev_id = arvif->vdev_id;
arg.enabled = 1;
arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
ret = ath10k_wmi_sta_keepalive(ar, &arg);
if (ret) {
ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
return 0;
}
/**********************/
/* Station management */
/**********************/
@ -1423,6 +1603,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
return;
arg->peer_flags |= WMI_PEER_VHT;
if (ar->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
arg->peer_flags |= WMI_PEER_VHT_2G;
arg->peer_vht_caps = vht_cap->cap;
ampdu_factor = (vht_cap->cap &
@ -1501,7 +1685,12 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
switch (ar->hw->conf.chandef.chan->band) {
case IEEE80211_BAND_2GHZ:
if (sta->ht_cap.ht_supported) {
if (sta->vht_cap.vht_supported) {
if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11AC_VHT40;
else
phymode = MODE_11AC_VHT20;
} else if (sta->ht_cap.ht_supported) {
if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11NG_HT40;
else
@ -1683,7 +1872,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
ath10k_warn(ar, "faield to down vdev %i: %d\n",
arvif->vdev_id, ret);
arvif->def_wep_key_idx = 0;
arvif->def_wep_key_idx = -1;
arvif->is_up = false;
}
@ -1742,11 +1932,14 @@ static int ath10k_station_assoc(struct ath10k *ar,
}
}
ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
if (ret) {
ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
/* Plumb cached keys only for static WEP */
if (arvif->def_wep_key_idx != -1) {
ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
if (ret) {
ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
}
@ -2011,75 +2204,13 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
* used only for CQM purposes (e.g. hostapd station keepalive ping) so
* it is safe to downgrade to NullFunc.
*/
hdr = (void *)skb->data;
if (ieee80211_is_qos_nullfunc(hdr->frame_control)) {
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
}
}
static void ath10k_tx_wep_key_work(struct work_struct *work)
{
struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
wep_key_work);
struct ath10k *ar = arvif->ar;
int ret, keyidx = arvif->def_wep_key_newidx;
mutex_lock(&arvif->ar->conf_mutex);
if (arvif->ar->state != ATH10K_STATE_ON)
goto unlock;
if (arvif->def_wep_key_idx == keyidx)
goto unlock;
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
arvif->vdev_id, keyidx);
ret = ath10k_wmi_vdev_set_param(arvif->ar,
arvif->vdev_id,
arvif->ar->wmi.vdev_param->def_keyid,
keyidx);
if (ret) {
ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
arvif->vdev_id,
ret);
goto unlock;
}
arvif->def_wep_key_idx = keyidx;
unlock:
mutex_unlock(&arvif->ar->conf_mutex);
}
static void ath10k_tx_h_update_wep_key(struct ieee80211_vif *vif,
struct ieee80211_key_conf *key,
struct sk_buff *skb)
{
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k *ar = arvif->ar;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_has_protected(hdr->frame_control))
return;
if (!key)
return;
if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
key->cipher != WLAN_CIPHER_SUITE_WEP104)
return;
if (key->keyidx == arvif->def_wep_key_idx)
return;
/* FIXME: Most likely a few frames will be TXed with an old key. Simply
* queueing frames until key index is updated is not an option because
* sk_buff may need more processing to be done, e.g. offchannel */
arvif->def_wep_key_newidx = key->keyidx;
ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
}
static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
struct ieee80211_vif *vif,
struct sk_buff *skb)
@ -2231,7 +2362,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
3 * HZ);
if (ret <= 0)
if (ret == 0)
ath10k_warn(ar, "timed out waiting for offchannel skb %p\n",
skb);
@ -2293,6 +2424,7 @@ void __ath10k_scan_finish(struct ath10k *ar)
case ATH10K_SCAN_RUNNING:
if (ar->scan.is_roc)
ieee80211_remain_on_channel_expired(ar->hw);
/* fall through */
case ATH10K_SCAN_ABORTING:
if (!ar->scan.is_roc)
ieee80211_scan_completed(ar->hw,
@ -2439,7 +2571,6 @@ static void ath10k_tx(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_key_conf *key = info->control.hw_key;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
/* We should disable CCK RATE due to P2P */
@ -2453,7 +2584,6 @@ static void ath10k_tx(struct ieee80211_hw *hw,
/* it makes no sense to process injected frames like that */
if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
ath10k_tx_h_nwifi(hw, skb);
ath10k_tx_h_update_wep_key(vif, key, skb);
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
ath10k_tx_h_seq_no(vif, skb);
}
@ -2960,7 +3090,6 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->ar = ar;
arvif->vif = vif;
INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
INIT_LIST_HEAD(&arvif->list);
if (ar->free_vdev_map == 0) {
@ -3049,15 +3178,18 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
list_add(&arvif->list, &ar->arvifs);
vdev_param = ar->wmi.vdev_param->def_keyid;
ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
arvif->def_wep_key_idx);
/* It makes no sense to have firmware do keepalives. mac80211 already
* takes care of this with idle connection polling.
*/
ret = ath10k_mac_vif_disable_keepalive(arvif);
if (ret) {
ath10k_warn(ar, "failed to set vdev %i default key id: %d\n",
ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
arvif->vdev_id, ret);
goto err_vdev_delete;
}
arvif->def_wep_key_idx = -1;
vdev_param = ar->wmi.vdev_param->tx_encap_type;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
ATH10K_HW_TXRX_NATIVE_WIFI);
@ -3176,8 +3308,6 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret;
cancel_work_sync(&arvif->wep_key_work);
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
@ -3288,9 +3418,21 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
if (ret)
ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
arvif->vdev_id, ret);
ret = ath10k_mac_setup_bcn_tmpl(arvif);
if (ret)
ath10k_warn(ar, "failed to update beacon template: %d\n",
ret);
}
if (changed & BSS_CHANGED_BEACON_INFO) {
if (changed & BSS_CHANGED_AP_PROBE_RESP) {
ret = ath10k_mac_setup_prb_tmpl(arvif);
if (ret)
ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
arvif->vdev_id, ret);
}
if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
arvif->dtim_period = info->dtim_period;
ath10k_dbg(ar, ATH10K_DBG_MAC,
@ -3537,6 +3679,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
const u8 *peer_addr;
bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
key->cipher == WLAN_CIPHER_SUITE_WEP104;
bool def_idx = false;
int ret = 0;
if (key->keyidx > WMI_MAX_KEY_INDEX)
@ -3582,7 +3725,14 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ath10k_clear_vdev_key(arvif, key);
}
ret = ath10k_install_key(arvif, key, cmd, peer_addr);
/* set TX_USAGE flag for all the keys incase of dot1x-WEP. For
* static WEP, do not set this flag for the keys whose key id
* is greater than default key id.
*/
if (arvif->def_wep_key_idx == -1)
def_idx = true;
ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx);
if (ret) {
ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
arvif->vdev_id, peer_addr, ret);
@ -3607,6 +3757,39 @@ exit:
return ret;
}
static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
int keyidx)
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret;
mutex_lock(&arvif->ar->conf_mutex);
if (arvif->ar->state != ATH10K_STATE_ON)
goto unlock;
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
arvif->vdev_id, keyidx);
ret = ath10k_wmi_vdev_set_param(arvif->ar,
arvif->vdev_id,
arvif->ar->wmi.vdev_param->def_keyid,
keyidx);
if (ret) {
ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
arvif->vdev_id,
ret);
goto unlock;
}
arvif->def_wep_key_idx = keyidx;
unlock:
mutex_unlock(&arvif->ar->conf_mutex);
}
static void ath10k_sta_rc_update_wk(struct work_struct *wk)
{
struct ath10k *ar;
@ -3842,6 +4025,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
u16 ac, bool enable)
{
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct wmi_sta_uapsd_auto_trig_arg arg = {};
u32 prio = 0, acc = 0;
u32 value = 0;
int ret = 0;
@ -3854,18 +4039,26 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
case IEEE80211_AC_VO:
value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
prio = 7;
acc = 3;
break;
case IEEE80211_AC_VI:
value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
prio = 5;
acc = 2;
break;
case IEEE80211_AC_BE:
value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
prio = 2;
acc = 1;
break;
case IEEE80211_AC_BK:
value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
prio = 0;
acc = 0;
break;
}
@ -3907,6 +4100,29 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
return ret;
}
if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) {
/* Only userspace can make an educated decision when to send
* trigger frame. The following effectively disables u-UAPSD
* autotrigger in firmware (which is enabled by default
* provided the autotrigger service is available).
*/
arg.wmm_ac = acc;
arg.user_priority = prio;
arg.service_interval = 0;
arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
arvif->bssid, &arg, 1);
if (ret) {
ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
ret);
return ret;
}
}
exit:
return ret;
}
@ -3916,6 +4132,7 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
const struct ieee80211_tx_queue_params *params)
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct wmi_wmm_params_arg *p = NULL;
int ret;
@ -3923,16 +4140,16 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
switch (ac) {
case IEEE80211_AC_VO:
p = &ar->wmm_params.ac_vo;
p = &arvif->wmm_params.ac_vo;
break;
case IEEE80211_AC_VI:
p = &ar->wmm_params.ac_vi;
p = &arvif->wmm_params.ac_vi;
break;
case IEEE80211_AC_BE:
p = &ar->wmm_params.ac_be;
p = &arvif->wmm_params.ac_be;
break;
case IEEE80211_AC_BK:
p = &ar->wmm_params.ac_bk;
p = &arvif->wmm_params.ac_bk;
break;
}
@ -3952,11 +4169,23 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
*/
p->txop = params->txop * 32;
/* FIXME: FW accepts wmm params per hw, not per vif */
ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
if (ret) {
ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
goto exit;
if (ar->wmi.ops->gen_vdev_wmm_conf) {
ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
&arvif->wmm_params);
if (ret) {
ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
arvif->vdev_id, ret);
goto exit;
}
} else {
/* This won't work well with multi-interface cases but it's
* better than nothing.
*/
ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params);
if (ret) {
ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
goto exit;
}
}
ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
@ -4726,6 +4955,7 @@ static const struct ieee80211_ops ath10k_ops = {
.hw_scan = ath10k_hw_scan,
.cancel_hw_scan = ath10k_cancel_hw_scan,
.set_key = ath10k_set_key,
.set_default_unicast_key = ath10k_set_default_unicast_key,
.sta_state = ath10k_sta_state,
.conf_tx = ath10k_conf_tx,
.remain_on_channel = ath10k_remain_on_channel,
@ -4751,6 +4981,9 @@ static const struct ieee80211_ops ath10k_ops = {
.suspend = ath10k_suspend,
.resume = ath10k_resume,
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
.sta_add_debugfs = ath10k_sta_add_debugfs,
#endif
};
#define RATETAB_ENT(_rate, _rateid, _flags) { \
@ -5074,7 +5307,8 @@ int ath10k_mac_register(struct ath10k *ar)
band->bitrates = ath10k_g_rates;
band->ht_cap = ht_cap;
/* vht is not supported in 2.4 GHz */
/* Enable the VHT support at 2.4 GHz */
band->vht_cap = vht_cap;
ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
}
@ -5139,6 +5373,19 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
/* Firmware delivers WPS/P2P Probe Requests frames to driver so
* that userspace (e.g. wpa_supplicant/hostapd) can generate
* correct Probe Responses. This is more of a hack advert..
*/
ar->hw->wiphy->probe_resp_offload |=
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
}
ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
ar->hw->wiphy->max_remain_on_channel_duration = 5000;

View file

@ -58,9 +58,11 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
#define QCA988X_2_0_DEVICE_ID (0x003c)
#define QCA6174_2_1_DEVICE_ID (0x003e)
static const struct pci_device_id ath10k_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
{ PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */
{0}
};
@ -70,6 +72,11 @@ static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = {
* because of that.
*/
{ QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV },
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_1_CHIP_ID_REV },
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_2_CHIP_ID_REV },
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_0_CHIP_ID_REV },
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_1_CHIP_ID_REV },
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV },
};
static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
@ -403,7 +410,7 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
return -EIO;
}
ATH10K_SKB_CB(skb)->paddr = paddr;
ATH10K_SKB_RXCB(skb)->paddr = paddr;
ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
if (ret) {
@ -872,7 +879,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
&flags) == 0) {
skb = transfer_context;
max_nbytes = skb->len + skb_tailroom(skb);
dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
if (unlikely(max_nbytes < nbytes)) {
@ -1238,7 +1245,7 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
ce_ring->per_transfer_context[i] = NULL;
dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
@ -1506,6 +1513,35 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
return 0;
}
static int ath10k_pci_get_num_banks(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
switch (ar_pci->pdev->device) {
case QCA988X_2_0_DEVICE_ID:
return 1;
case QCA6174_2_1_DEVICE_ID:
switch (MS(ar->chip_id, SOC_CHIP_ID_REV)) {
case QCA6174_HW_1_0_CHIP_ID_REV:
case QCA6174_HW_1_1_CHIP_ID_REV:
return 3;
case QCA6174_HW_1_3_CHIP_ID_REV:
return 2;
case QCA6174_HW_2_1_CHIP_ID_REV:
case QCA6174_HW_2_2_CHIP_ID_REV:
return 6;
case QCA6174_HW_3_0_CHIP_ID_REV:
case QCA6174_HW_3_1_CHIP_ID_REV:
case QCA6174_HW_3_2_CHIP_ID_REV:
return 9;
}
break;
}
ath10k_warn(ar, "unknown number of banks, assuming 1\n");
return 1;
}
static int ath10k_pci_init_config(struct ath10k *ar)
{
u32 interconnect_targ_addr;
@ -1616,7 +1652,8 @@ static int ath10k_pci_init_config(struct ath10k *ar)
/* first bank is switched to IRAM */
ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
HI_EARLY_ALLOC_MAGIC_MASK);
ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
ealloc_value |= ((ath10k_pci_get_num_banks(ar) <<
HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
HI_EARLY_ALLOC_IRAM_BANKS_MASK);
ret = ath10k_pci_diag_write32(ar, ealloc_targ_addr, ealloc_value);
@ -1812,12 +1849,12 @@ static int ath10k_pci_warm_reset(struct ath10k *ar)
return 0;
}
static int ath10k_pci_chip_reset(struct ath10k *ar)
static int ath10k_pci_qca988x_chip_reset(struct ath10k *ar)
{
int i, ret;
u32 val;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset\n");
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot 988x chip reset\n");
/* Some hardware revisions (e.g. CUS223v2) has issues with cold reset.
* It is thus preferred to use warm reset which is safer but may not be
@ -1881,11 +1918,53 @@ static int ath10k_pci_chip_reset(struct ath10k *ar)
return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (cold)\n");
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca988x chip reset complete (cold)\n");
return 0;
}
static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar)
{
int ret;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset\n");
/* FIXME: QCA6174 requires cold + warm reset to work. */
ret = ath10k_pci_cold_reset(ar);
if (ret) {
ath10k_warn(ar, "failed to cold reset: %d\n", ret);
return ret;
}
ret = ath10k_pci_wait_for_target_init(ar);
if (ret) {
ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
ret);
return ret;
}
ret = ath10k_pci_warm_reset(ar);
if (ret) {
ath10k_warn(ar, "failed to warm reset: %d\n", ret);
return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset complete (cold)\n");
return 0;
}
static int ath10k_pci_chip_reset(struct ath10k *ar)
{
if (QCA_REV_988X(ar))
return ath10k_pci_qca988x_chip_reset(ar);
else if (QCA_REV_6174(ar))
return ath10k_pci_qca6174_chip_reset(ar);
else
return -ENOTSUPP;
}
static int ath10k_pci_hif_power_up(struct ath10k *ar)
{
int ret;
@ -1910,6 +1989,12 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar)
*/
ret = ath10k_pci_chip_reset(ar);
if (ret) {
if (ath10k_pci_has_fw_crashed(ar)) {
ath10k_warn(ar, "firmware crashed during chip reset\n");
ath10k_pci_fw_crashed_clear(ar);
ath10k_pci_fw_crashed_dump(ar);
}
ath10k_err(ar, "failed to reset chip: %d\n", ret);
goto err_sleep;
}
@ -2041,6 +2126,7 @@ static void ath10k_msi_err_tasklet(unsigned long data)
return;
}
ath10k_pci_irq_disable(ar);
ath10k_pci_fw_crashed_clear(ar);
ath10k_pci_fw_crashed_dump(ar);
}
@ -2110,6 +2196,7 @@ static void ath10k_pci_tasklet(unsigned long data)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
if (ath10k_pci_has_fw_crashed(ar)) {
ath10k_pci_irq_disable(ar);
ath10k_pci_fw_crashed_clear(ar);
ath10k_pci_fw_crashed_dump(ar);
return;
@ -2352,8 +2439,6 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
if (val & FW_IND_EVENT_PENDING) {
ath10k_warn(ar, "device has crashed during init\n");
ath10k_pci_fw_crashed_clear(ar);
ath10k_pci_fw_crashed_dump(ar);
return -ECOMM;
}
@ -2507,11 +2592,23 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
int ret = 0;
struct ath10k *ar;
struct ath10k_pci *ar_pci;
enum ath10k_hw_rev hw_rev;
u32 chip_id;
ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev,
ATH10K_BUS_PCI,
&ath10k_pci_hif_ops);
switch (pci_dev->device) {
case QCA988X_2_0_DEVICE_ID:
hw_rev = ATH10K_HW_QCA988X;
break;
case QCA6174_2_1_DEVICE_ID:
hw_rev = ATH10K_HW_QCA6174;
break;
default:
WARN_ON(1);
return -ENOTSUPP;
}
ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, ATH10K_BUS_PCI,
hw_rev, &ath10k_pci_hif_ops);
if (!ar) {
dev_err(&pdev->dev, "failed to allocate core\n");
return -ENOMEM;
@ -2540,18 +2637,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_release;
}
chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
if (chip_id == 0xffffffff) {
ath10k_err(ar, "failed to get chip id\n");
goto err_sleep;
}
if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) {
ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n",
pdev->device, chip_id);
goto err_sleep;
}
ret = ath10k_pci_alloc_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
@ -2578,6 +2663,24 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_deinit_irq;
}
ret = ath10k_pci_chip_reset(ar);
if (ret) {
ath10k_err(ar, "failed to reset chip: %d\n", ret);
goto err_free_irq;
}
chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
if (chip_id == 0xffffffff) {
ath10k_err(ar, "failed to get chip id\n");
goto err_free_irq;
}
if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) {
ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n",
pdev->device, chip_id);
goto err_sleep;
}
ath10k_pci_sleep(ar);
ret = ath10k_core_register(ar, chip_id);

View file

@ -194,7 +194,7 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
#define ATH10K_PCI_RX_POST_RETRY_MS 50
#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */
#define PCIE_WAKE_TIMEOUT 10000 /* 10ms */
#define BAR_NUM 0

View file

@ -850,7 +850,7 @@ struct rx_ppdu_start {
#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
struct rx_ppdu_end {
struct rx_ppdu_end_common {
__le32 evm_p0;
__le32 evm_p1;
__le32 evm_p2;
@ -873,10 +873,33 @@ struct rx_ppdu_end {
u8 phy_err_code;
__le16 flags; /* %RX_PPDU_END_FLAGS_ */
__le32 info0; /* %RX_PPDU_END_INFO0_ */
} __packed;
struct rx_ppdu_end_qca988x {
__le16 bb_length;
__le16 info1; /* %RX_PPDU_END_INFO1_ */
} __packed;
#define RX_PPDU_END_RTT_CORRELATION_VALUE_MASK 0x00ffffff
#define RX_PPDU_END_RTT_CORRELATION_VALUE_LSB 0
#define RX_PPDU_END_RTT_UNUSED_MASK 0x7f000000
#define RX_PPDU_END_RTT_UNUSED_LSB 24
#define RX_PPDU_END_RTT_NORMAL_MODE BIT(31)
struct rx_ppdu_end_qca6174 {
__le32 rtt; /* %RX_PPDU_END_RTT_ */
__le16 bb_length;
__le16 info1; /* %RX_PPDU_END_INFO1_ */
} __packed;
struct rx_ppdu_end {
struct rx_ppdu_end_common common;
union {
struct rx_ppdu_end_qca988x qca988x;
struct rx_ppdu_end_qca6174 qca6174;
} __packed;
} __packed;
/*
* evm_p0
* EVM for pilot 0. Contain EVM for streams: 0, 1, 2 and 3.

View file

@ -18,6 +18,8 @@
#ifndef __TARGADDRS_H__
#define __TARGADDRS_H__
#include "hw.h"
/*
* xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
* host_interest structure. It must match the address of the _host_interest
@ -445,4 +447,7 @@ Fw Mode/SubMode Mask
#define QCA988X_BOARD_DATA_SZ 7168
#define QCA988X_BOARD_EXT_DATA_SZ 0
#define QCA6174_BOARD_DATA_SZ 8192
#define QCA6174_BOARD_EXT_DATA_SZ 0
#endif /* __TARGADDRS_H__ */

View file

@ -98,7 +98,7 @@ static int ath10k_thermal_set_cur_dutycycle(struct thermal_cooling_device *cdev,
}
period = max(ATH10K_QUIET_PERIOD_MIN,
(ATH10K_QUIET_PERIOD_DEFAULT / num_bss));
duration = period * (duty_cycle / 100);
duration = (period * duty_cycle) / 100;
enabled = duration ? 1 : 0;
ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
@ -160,7 +160,8 @@ static ssize_t ath10k_thermal_show_temp(struct device *dev,
temperature = ar->thermal.temperature;
spin_unlock_bh(&ar->data_lock);
ret = snprintf(buf, PAGE_SIZE, "%d", temperature);
/* display in millidegree celcius */
ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
out:
mutex_unlock(&ar->conf_mutex);
return ret;
@ -215,7 +216,7 @@ int ath10k_thermal_register(struct ath10k *ar)
/* Avoid linking error on devm_hwmon_device_register_with_groups, I
* guess linux/hwmon.h is missing proper stubs. */
if (!config_enabled(HWMON))
if (!config_enabled(CONFIG_HWMON))
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,

View file

@ -453,6 +453,74 @@ TRACE_EVENT(ath10k_htt_rx_desc,
)
);
TRACE_EVENT(ath10k_wmi_diag_container,
TP_PROTO(struct ath10k *ar,
u8 type,
u32 timestamp,
u32 code,
u16 len,
const void *data),
TP_ARGS(ar, type, timestamp, code, len, data),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u8, type)
__field(u32, timestamp)
__field(u32, code)
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->type = type;
__entry->timestamp = timestamp;
__entry->code = code;
__entry->len = len;
memcpy(__get_dynamic_array(data), data, len);
),
TP_printk(
"%s %s diag container type %hhu timestamp %u code %u len %d",
__get_str(driver),
__get_str(device),
__entry->type,
__entry->timestamp,
__entry->code,
__entry->len
)
);
TRACE_EVENT(ath10k_wmi_diag,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->len = len;
memcpy(__get_dynamic_array(data), data, len);
),
TP_printk(
"%s %s tlv diag len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */

View file

@ -64,7 +64,13 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
return;
}
msdu = htt->pending_tx[tx_done->msdu_id];
msdu = idr_find(&htt->pending_tx, tx_done->msdu_id);
if (!msdu) {
ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n",
tx_done->msdu_id);
return;
}
skb_cb = ATH10K_SKB_CB(msdu);
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
@ -95,7 +101,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
/* we do not own the msdu anymore */
exit:
htt->pending_tx[tx_done->msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
__ath10k_htt_tx_dec_pending(htt);
if (htt->num_pending_tx == 0)

View file

@ -78,6 +78,8 @@ struct wmi_ops {
const struct wmi_vdev_spectral_conf_arg *arg);
struct sk_buff *(*gen_vdev_spectral_enable)(struct ath10k *ar, u32 vdev_id,
u32 trigger, u32 enable);
struct sk_buff *(*gen_vdev_wmm_conf)(struct ath10k *ar, u32 vdev_id,
const struct wmi_wmm_params_all_arg *arg);
struct sk_buff *(*gen_peer_create)(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN]);
struct sk_buff *(*gen_peer_delete)(struct ath10k *ar, u32 vdev_id,
@ -102,16 +104,20 @@ struct wmi_ops {
u32 value);
struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar,
const struct wmi_scan_chan_list_arg *arg);
struct sk_buff *(*gen_beacon_dma)(struct ath10k_vif *arvif);
struct sk_buff *(*gen_beacon_dma)(struct ath10k *ar, u32 vdev_id,
const void *bcn, size_t bcn_len,
u32 bcn_paddr, bool dtim_zero,
bool deliver_cab);
struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg);
const struct wmi_wmm_params_all_arg *arg);
struct sk_buff *(*gen_request_stats)(struct ath10k *ar,
enum wmi_stats_id stats_id);
struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar,
enum wmi_force_fw_hang_type type,
u32 delay_ms);
struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb);
struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable);
struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable,
u32 log_level);
struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter);
struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar);
struct sk_buff *(*gen_pdev_set_quiet_mode)(struct ath10k *ar,
@ -119,6 +125,30 @@ struct wmi_ops {
u32 next_offset,
u32 enabled);
struct sk_buff *(*gen_pdev_get_temperature)(struct ath10k *ar);
struct sk_buff *(*gen_addba_clear_resp)(struct ath10k *ar, u32 vdev_id,
const u8 *mac);
struct sk_buff *(*gen_addba_send)(struct ath10k *ar, u32 vdev_id,
const u8 *mac, u32 tid, u32 buf_size);
struct sk_buff *(*gen_addba_set_resp)(struct ath10k *ar, u32 vdev_id,
const u8 *mac, u32 tid,
u32 status);
struct sk_buff *(*gen_delba_send)(struct ath10k *ar, u32 vdev_id,
const u8 *mac, u32 tid, u32 initiator,
u32 reason);
struct sk_buff *(*gen_bcn_tmpl)(struct ath10k *ar, u32 vdev_id,
u32 tim_ie_offset, struct sk_buff *bcn,
u32 prb_caps, u32 prb_erp,
void *prb_ies, size_t prb_ies_len);
struct sk_buff *(*gen_prb_tmpl)(struct ath10k *ar, u32 vdev_id,
struct sk_buff *bcn);
struct sk_buff *(*gen_p2p_go_bcn_ie)(struct ath10k *ar, u32 vdev_id,
const u8 *p2p_ie);
struct sk_buff *(*gen_vdev_sta_uapsd)(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN],
const struct wmi_sta_uapsd_auto_trig_arg *args,
u32 num_ac);
struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar,
const struct wmi_sta_keepalive_arg *arg);
};
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
@ -557,6 +587,42 @@ ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}
static inline int
ath10k_wmi_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN],
const struct wmi_sta_uapsd_auto_trig_arg *args,
u32 num_ac)
{
struct sk_buff *skb;
u32 cmd_id;
if (!ar->wmi.ops->gen_vdev_sta_uapsd)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_vdev_sta_uapsd(ar, vdev_id, peer_addr, args,
num_ac);
if (IS_ERR(skb))
return PTR_ERR(skb);
cmd_id = ar->wmi.cmd->sta_uapsd_auto_trig_cmdid;
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}
static inline int
ath10k_wmi_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
const struct wmi_wmm_params_all_arg *arg)
{
struct sk_buff *skb;
u32 cmd_id;
skb = ar->wmi.ops->gen_vdev_wmm_conf(ar, vdev_id, arg);
if (IS_ERR(skb))
return PTR_ERR(skb);
cmd_id = ar->wmi.cmd->vdev_set_wmm_params_cmdid;
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}
static inline int
ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN])
@ -706,16 +772,19 @@ ath10k_wmi_peer_assoc(struct ath10k *ar,
}
static inline int
ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
ath10k_wmi_beacon_send_ref_nowait(struct ath10k *ar, u32 vdev_id,
const void *bcn, size_t bcn_len,
u32 bcn_paddr, bool dtim_zero,
bool deliver_cab)
{
struct ath10k *ar = arvif->ar;
struct sk_buff *skb;
int ret;
if (!ar->wmi.ops->gen_beacon_dma)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_beacon_dma(arvif);
skb = ar->wmi.ops->gen_beacon_dma(ar, vdev_id, bcn, bcn_len, bcn_paddr,
dtim_zero, deliver_cab);
if (IS_ERR(skb))
return PTR_ERR(skb);
@ -731,7 +800,7 @@ ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
static inline int
ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg)
const struct wmi_wmm_params_all_arg *arg)
{
struct sk_buff *skb;
@ -778,14 +847,14 @@ ath10k_wmi_force_fw_hang(struct ath10k *ar,
}
static inline int
ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable, u32 log_level)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_dbglog_cfg)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable);
skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable, log_level);
if (IS_ERR(skb))
return PTR_ERR(skb);
@ -857,4 +926,139 @@ ath10k_wmi_pdev_get_temperature(struct ath10k *ar)
ar->wmi.cmd->pdev_get_temperature_cmdid);
}
static inline int
ath10k_wmi_addba_clear_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_addba_clear_resp)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_addba_clear_resp(ar, vdev_id, mac);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->addba_clear_resp_cmdid);
}
static inline int
ath10k_wmi_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 buf_size)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_addba_send)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_addba_send(ar, vdev_id, mac, tid, buf_size);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->addba_send_cmdid);
}
static inline int
ath10k_wmi_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 status)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_addba_set_resp)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_addba_set_resp(ar, vdev_id, mac, tid, status);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->addba_set_resp_cmdid);
}
static inline int
ath10k_wmi_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 initiator, u32 reason)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_delba_send)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_delba_send(ar, vdev_id, mac, tid, initiator,
reason);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->delba_send_cmdid);
}
static inline int
ath10k_wmi_bcn_tmpl(struct ath10k *ar, u32 vdev_id, u32 tim_ie_offset,
struct sk_buff *bcn, u32 prb_caps, u32 prb_erp,
void *prb_ies, size_t prb_ies_len)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_bcn_tmpl)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_bcn_tmpl(ar, vdev_id, tim_ie_offset, bcn,
prb_caps, prb_erp, prb_ies,
prb_ies_len);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->bcn_tmpl_cmdid);
}
static inline int
ath10k_wmi_prb_tmpl(struct ath10k *ar, u32 vdev_id, struct sk_buff *prb)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_prb_tmpl)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_prb_tmpl(ar, vdev_id, prb);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->prb_tmpl_cmdid);
}
static inline int
ath10k_wmi_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id, const u8 *p2p_ie)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_p2p_go_bcn_ie)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_p2p_go_bcn_ie(ar, vdev_id, p2p_ie);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->p2p_go_set_beacon_ie);
}
static inline int
ath10k_wmi_sta_keepalive(struct ath10k *ar,
const struct wmi_sta_keepalive_arg *arg)
{
struct sk_buff *skb;
u32 cmd_id;
if (!ar->wmi.ops->gen_sta_keepalive)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_sta_keepalive(ar, arg);
if (IS_ERR(skb))
return PTR_ERR(skb);
cmd_id = ar->wmi.cmd->sta_keepalive_cmd;
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}
#endif

View file

@ -58,6 +58,10 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
= { .min_len = sizeof(struct wlan_host_mem_req) },
[WMI_TLV_TAG_STRUCT_READY_EVENT]
= { .min_len = sizeof(struct wmi_tlv_rdy_ev) },
[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT]
= { .min_len = sizeof(struct wmi_tlv_bcn_tx_status_ev) },
[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT]
= { .min_len = sizeof(struct wmi_tlv_diag_data_ev) },
};
static int
@ -156,6 +160,142 @@ static u16 ath10k_wmi_tlv_len(const void *ptr)
return __le16_to_cpu((((const struct wmi_tlv *)ptr) - 1)->len);
}
/**************/
/* TLV events */
/**************/
static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar,
struct sk_buff *skb)
{
const void **tb;
const struct wmi_tlv_bcn_tx_status_ev *ev;
u32 vdev_id, tx_status;
int ret;
tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
return ret;
}
ev = tb[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT];
if (!ev) {
kfree(tb);
return -EPROTO;
}
tx_status = __le32_to_cpu(ev->tx_status);
vdev_id = __le32_to_cpu(ev->vdev_id);
switch (tx_status) {
case WMI_TLV_BCN_TX_STATUS_OK:
break;
case WMI_TLV_BCN_TX_STATUS_XRETRY:
case WMI_TLV_BCN_TX_STATUS_DROP:
case WMI_TLV_BCN_TX_STATUS_FILTERED:
/* FIXME: It's probably worth telling mac80211 to stop the
* interface as it is crippled.
*/
ath10k_warn(ar, "received bcn tmpl tx status on vdev %i: %d",
vdev_id, tx_status);
break;
}
kfree(tb);
return 0;
}
static int ath10k_wmi_tlv_event_diag_data(struct ath10k *ar,
struct sk_buff *skb)
{
const void **tb;
const struct wmi_tlv_diag_data_ev *ev;
const struct wmi_tlv_diag_item *item;
const void *data;
int ret, num_items, len;
tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
return ret;
}
ev = tb[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT];
data = tb[WMI_TLV_TAG_ARRAY_BYTE];
if (!ev || !data) {
kfree(tb);
return -EPROTO;
}
num_items = __le32_to_cpu(ev->num_items);
len = ath10k_wmi_tlv_len(data);
while (num_items--) {
if (len == 0)
break;
if (len < sizeof(*item)) {
ath10k_warn(ar, "failed to parse diag data: can't fit item header\n");
break;
}
item = data;
if (len < sizeof(*item) + __le16_to_cpu(item->len)) {
ath10k_warn(ar, "failed to parse diag data: item is too long\n");
break;
}
trace_ath10k_wmi_diag_container(ar,
item->type,
__le32_to_cpu(item->timestamp),
__le32_to_cpu(item->code),
__le16_to_cpu(item->len),
item->payload);
len -= sizeof(*item);
len -= roundup(__le16_to_cpu(item->len), 4);
data += sizeof(*item);
data += roundup(__le16_to_cpu(item->len), 4);
}
if (num_items != -1 || len != 0)
ath10k_warn(ar, "failed to parse diag data event: num_items %d len %d\n",
num_items, len);
kfree(tb);
return 0;
}
static int ath10k_wmi_tlv_event_diag(struct ath10k *ar,
struct sk_buff *skb)
{
const void **tb;
const void *data;
int ret, len;
tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
return ret;
}
data = tb[WMI_TLV_TAG_ARRAY_BYTE];
if (!data) {
kfree(tb);
return -EPROTO;
}
len = ath10k_wmi_tlv_len(data);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv diag event len %d\n", len);
trace_ath10k_wmi_diag(ar, data, len);
kfree(tb);
return 0;
}
/***********/
/* TLV ops */
/***********/
@ -268,6 +408,15 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_TLV_READY_EVENTID:
ath10k_wmi_event_ready(ar, skb);
break;
case WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID:
ath10k_wmi_tlv_event_bcn_tx_status(ar, skb);
break;
case WMI_TLV_DIAG_DATA_CONTAINER_EVENTID:
ath10k_wmi_tlv_event_diag_data(ar, skb);
break;
case WMI_TLV_DIAG_EVENTID:
ath10k_wmi_tlv_event_diag(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@ -903,8 +1052,15 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS);
cfg->num_offload_peers = __cpu_to_le32(0);
cfg->num_offload_reorder_bufs = __cpu_to_le32(0);
if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) {
cfg->num_offload_peers = __cpu_to_le32(3);
cfg->num_offload_reorder_bufs = __cpu_to_le32(3);
} else {
cfg->num_offload_peers = __cpu_to_le32(0);
cfg->num_offload_reorder_bufs = __cpu_to_le32(0);
}
cfg->num_peer_keys = __cpu_to_le32(2);
cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS);
cfg->ast_skid_limit = __cpu_to_le32(0x10);
@ -1356,6 +1512,173 @@ ath10k_wmi_tlv_op_gen_vdev_install_key(struct ath10k *ar,
return skb;
}
static void *ath10k_wmi_tlv_put_uapsd_ac(struct ath10k *ar, void *ptr,
const struct wmi_sta_uapsd_auto_trig_arg *arg)
{
struct wmi_sta_uapsd_auto_trig_param *ac;
struct wmi_tlv *tlv;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM);
tlv->len = __cpu_to_le16(sizeof(*ac));
ac = (void *)tlv->value;
ac->wmm_ac = __cpu_to_le32(arg->wmm_ac);
ac->user_priority = __cpu_to_le32(arg->user_priority);
ac->service_interval = __cpu_to_le32(arg->service_interval);
ac->suspend_interval = __cpu_to_le32(arg->suspend_interval);
ac->delay_interval = __cpu_to_le32(arg->delay_interval);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi tlv vdev sta uapsd auto trigger ac %d prio %d svc int %d susp int %d delay int %d\n",
ac->wmm_ac, ac->user_priority, ac->service_interval,
ac->suspend_interval, ac->delay_interval);
return ptr + sizeof(*tlv) + sizeof(*ac);
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN],
const struct wmi_sta_uapsd_auto_trig_arg *args,
u32 num_ac)
{
struct wmi_sta_uapsd_auto_trig_cmd_fixed_param *cmd;
struct wmi_sta_uapsd_auto_trig_param *ac;
struct wmi_tlv *tlv;
struct sk_buff *skb;
size_t len;
size_t ac_tlv_len;
void *ptr;
int i;
ac_tlv_len = num_ac * (sizeof(*tlv) + sizeof(*ac));
len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) + ac_tlv_len;
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->num_ac = __cpu_to_le32(num_ac);
ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
tlv->len = __cpu_to_le16(ac_tlv_len);
ac = (void *)tlv->value;
ptr += sizeof(*tlv);
for (i = 0; i < num_ac; i++)
ptr = ath10k_wmi_tlv_put_uapsd_ac(ar, ptr, &args[i]);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev sta uapsd auto trigger\n");
return skb;
}
static void *ath10k_wmi_tlv_put_wmm(void *ptr,
const struct wmi_wmm_params_arg *arg)
{
struct wmi_wmm_params *wmm;
struct wmi_tlv *tlv;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS);
tlv->len = __cpu_to_le16(sizeof(*wmm));
wmm = (void *)tlv->value;
ath10k_wmi_set_wmm_param(wmm, arg);
return ptr + sizeof(*tlv) + sizeof(*wmm);
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
const struct wmi_wmm_params_all_arg *arg)
{
struct wmi_tlv_vdev_set_wmm_cmd *cmd;
struct wmi_wmm_params *wmm;
struct wmi_tlv *tlv;
struct sk_buff *skb;
size_t len;
void *ptr;
len = (sizeof(*tlv) + sizeof(*cmd)) +
(4 * (sizeof(*tlv) + sizeof(*wmm)));
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be);
ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk);
ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi);
ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev wmm conf\n");
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_sta_keepalive(struct ath10k *ar,
const struct wmi_sta_keepalive_arg *arg)
{
struct wmi_tlv_sta_keepalive_cmd *cmd;
struct wmi_sta_keepalive_arp_resp *arp;
struct sk_buff *skb;
struct wmi_tlv *tlv;
void *ptr;
size_t len;
len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) + sizeof(*arp);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
cmd->enabled = __cpu_to_le32(arg->enabled);
cmd->method = __cpu_to_le32(arg->method);
cmd->interval = __cpu_to_le32(arg->interval);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE);
tlv->len = __cpu_to_le16(sizeof(*arp));
arp = (void *)tlv->value;
arp->src_ip4_addr = arg->src_ip4_addr;
arp->dest_ip4_addr = arg->dest_ip4_addr;
ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv sta keepalive vdev %d enabled %d method %d inverval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
const u8 peer_addr[ETH_ALEN])
@ -1678,13 +2001,15 @@ ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar,
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k_vif *arvif)
ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id,
const void *bcn, size_t bcn_len,
u32 bcn_paddr, bool dtim_zero,
bool deliver_cab)
{
struct ath10k *ar = arvif->ar;
struct wmi_bcn_tx_ref_cmd *cmd;
struct wmi_tlv *tlv;
struct sk_buff *skb;
struct sk_buff *beacon = arvif->beacon;
struct ieee80211_hdr *hdr;
u16 fc;
@ -1692,48 +2017,33 @@ ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k_vif *arvif)
if (!skb)
return ERR_PTR(-ENOMEM);
hdr = (struct ieee80211_hdr *)beacon->data;
hdr = (struct ieee80211_hdr *)bcn;
fc = le16_to_cpu(hdr->frame_control);
tlv = (void *)skb->data;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
cmd->data_len = __cpu_to_le32(beacon->len);
cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->data_len = __cpu_to_le32(bcn_len);
cmd->data_ptr = __cpu_to_le32(bcn_paddr);
cmd->msdu_id = 0;
cmd->frame_control = __cpu_to_le32(fc);
cmd->flags = 0;
if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
if (dtim_zero)
cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
if (deliver_cab)
cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv beacon dma\n");
return skb;
}
static void *ath10k_wmi_tlv_put_wmm(void *ptr,
const struct wmi_wmm_params_arg *arg)
{
struct wmi_wmm_params *wmm;
struct wmi_tlv *tlv;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS);
tlv->len = __cpu_to_le16(sizeof(*wmm));
wmm = (void *)tlv->value;
ath10k_wmi_pdev_set_wmm_param(wmm, arg);
return ptr + sizeof(*tlv) + sizeof(*wmm);
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg)
const struct wmi_wmm_params_all_arg *arg)
{
struct wmi_tlv_pdev_set_wmm_cmd *cmd;
struct wmi_wmm_params *wmm;
@ -1816,8 +2126,8 @@ ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar,
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable)
{
ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
u32 log_level) {
struct wmi_tlv_dbglog_cmd *cmd;
struct wmi_tlv *tlv;
struct sk_buff *skb;
@ -1922,6 +2232,159 @@ ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar)
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_bcn_tmpl(struct ath10k *ar, u32 vdev_id,
u32 tim_ie_offset, struct sk_buff *bcn,
u32 prb_caps, u32 prb_erp, void *prb_ies,
size_t prb_ies_len)
{
struct wmi_tlv_bcn_tmpl_cmd *cmd;
struct wmi_tlv_bcn_prb_info *info;
struct wmi_tlv *tlv;
struct sk_buff *skb;
void *ptr;
size_t len;
if (WARN_ON(prb_ies_len > 0 && !prb_ies))
return ERR_PTR(-EINVAL);
len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) + sizeof(*info) + prb_ies_len +
sizeof(*tlv) + roundup(bcn->len, 4);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->tim_ie_offset = __cpu_to_le32(tim_ie_offset);
cmd->buf_len = __cpu_to_le32(bcn->len);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
/* FIXME: prb_ies_len should be probably aligned to 4byte boundary but
* then it is then impossible to pass original ie len.
* This chunk is not used yet so if setting probe resp template yields
* problems with beaconing or crashes firmware look here.
*/
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
tlv->len = __cpu_to_le16(sizeof(*info) + prb_ies_len);
info = (void *)tlv->value;
info->caps = __cpu_to_le32(prb_caps);
info->erp = __cpu_to_le32(prb_erp);
memcpy(info->ies, prb_ies, prb_ies_len);
ptr += sizeof(*tlv);
ptr += sizeof(*info);
ptr += prb_ies_len;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(roundup(bcn->len, 4));
memcpy(tlv->value, bcn->data, bcn->len);
/* FIXME: Adjust TSF? */
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv bcn tmpl vdev_id %i\n",
vdev_id);
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_prb_tmpl(struct ath10k *ar, u32 vdev_id,
struct sk_buff *prb)
{
struct wmi_tlv_prb_tmpl_cmd *cmd;
struct wmi_tlv_bcn_prb_info *info;
struct wmi_tlv *tlv;
struct sk_buff *skb;
void *ptr;
size_t len;
len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) + sizeof(*info) +
sizeof(*tlv) + roundup(prb->len, 4);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->buf_len = __cpu_to_le32(prb->len);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
tlv->len = __cpu_to_le16(sizeof(*info));
info = (void *)tlv->value;
info->caps = 0;
info->erp = 0;
ptr += sizeof(*tlv);
ptr += sizeof(*info);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(roundup(prb->len, 4));
memcpy(tlv->value, prb->data, prb->len);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv prb tmpl vdev_id %i\n",
vdev_id);
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id,
const u8 *p2p_ie)
{
struct wmi_tlv_p2p_go_bcn_ie *cmd;
struct wmi_tlv *tlv;
struct sk_buff *skb;
void *ptr;
size_t len;
len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) + roundup(p2p_ie[1] + 2, 4);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->ie_len = __cpu_to_le32(p2p_ie[1] + 2);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(roundup(p2p_ie[1] + 2, 4));
memcpy(tlv->value, p2p_ie, p2p_ie[1] + 2);
ptr += sizeof(*tlv);
ptr += roundup(p2p_ie[1] + 2, 4);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv p2p go bcn ie for vdev %i\n",
vdev_id);
return skb;
}
/****************/
/* TLV mappings */
/****************/
@ -2045,6 +2508,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = {
.gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID,
.gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID,
.pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED,
.vdev_set_wmm_params_cmdid = WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
};
static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = {
@ -2188,6 +2652,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_vdev_down = ath10k_wmi_tlv_op_gen_vdev_down,
.gen_vdev_set_param = ath10k_wmi_tlv_op_gen_vdev_set_param,
.gen_vdev_install_key = ath10k_wmi_tlv_op_gen_vdev_install_key,
.gen_vdev_wmm_conf = ath10k_wmi_tlv_op_gen_vdev_wmm_conf,
.gen_peer_create = ath10k_wmi_tlv_op_gen_peer_create,
.gen_peer_delete = ath10k_wmi_tlv_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_tlv_op_gen_peer_flush,
@ -2207,6 +2672,15 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,
/* .gen_pdev_set_quiet_mode not implemented */
/* .gen_pdev_get_temperature not implemented */
/* .gen_addba_clear_resp not implemented */
/* .gen_addba_send not implemented */
/* .gen_addba_set_resp not implemented */
/* .gen_delba_send not implemented */
.gen_bcn_tmpl = ath10k_wmi_tlv_op_gen_bcn_tmpl,
.gen_prb_tmpl = ath10k_wmi_tlv_op_gen_prb_tmpl,
.gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie,
.gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd,
.gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive,
};
/************/

View file

@ -1302,6 +1302,10 @@ struct wmi_tlv_pdev_set_wmm_cmd {
__le32 dg_type; /* no idea.. */
} __packed;
struct wmi_tlv_vdev_set_wmm_cmd {
__le32 vdev_id;
} __packed;
struct wmi_tlv_phyerr_ev {
__le32 num_phyerrs;
__le32 tsf_l32;
@ -1375,6 +1379,66 @@ struct wmi_tlv_pktlog_disable {
__le32 reserved;
} __packed;
enum wmi_tlv_bcn_tx_status {
WMI_TLV_BCN_TX_STATUS_OK,
WMI_TLV_BCN_TX_STATUS_XRETRY,
WMI_TLV_BCN_TX_STATUS_DROP,
WMI_TLV_BCN_TX_STATUS_FILTERED,
};
struct wmi_tlv_bcn_tx_status_ev {
__le32 vdev_id;
__le32 tx_status;
} __packed;
struct wmi_tlv_bcn_prb_info {
__le32 caps;
__le32 erp;
u8 ies[0];
} __packed;
struct wmi_tlv_bcn_tmpl_cmd {
__le32 vdev_id;
__le32 tim_ie_offset;
__le32 buf_len;
} __packed;
struct wmi_tlv_prb_tmpl_cmd {
__le32 vdev_id;
__le32 buf_len;
} __packed;
struct wmi_tlv_p2p_go_bcn_ie {
__le32 vdev_id;
__le32 ie_len;
} __packed;
enum wmi_tlv_diag_item_type {
WMI_TLV_DIAG_ITEM_TYPE_FW_EVENT,
WMI_TLV_DIAG_ITEM_TYPE_FW_LOG,
WMI_TLV_DIAG_ITEM_TYPE_FW_DEBUG_MSG,
};
struct wmi_tlv_diag_item {
u8 type;
u8 reserved;
__le16 len;
__le32 timestamp;
__le32 code;
u8 payload[0];
} __packed;
struct wmi_tlv_diag_data_ev {
__le32 num_items;
} __packed;
struct wmi_tlv_sta_keepalive_cmd {
__le32 vdev_id;
__le32 enabled;
__le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
__le32 interval; /* in seconds */
} __packed;
void ath10k_wmi_tlv_attach(struct ath10k *ar);
#endif

View file

@ -956,23 +956,45 @@ err_pull:
static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
struct ath10k_skb_cb *cb;
struct sk_buff *bcn;
int ret;
lockdep_assert_held(&arvif->ar->data_lock);
spin_lock_bh(&ar->data_lock);
if (arvif->beacon == NULL)
return;
bcn = arvif->beacon;
if (arvif->beacon_sent)
return;
if (!bcn)
goto unlock;
ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
if (ret)
return;
cb = ATH10K_SKB_CB(bcn);
/* We need to retain the arvif->beacon reference for DMA unmapping and
* freeing the skbuff later. */
arvif->beacon_sent = true;
switch (arvif->beacon_state) {
case ATH10K_BEACON_SENDING:
case ATH10K_BEACON_SENT:
break;
case ATH10K_BEACON_SCHEDULED:
arvif->beacon_state = ATH10K_BEACON_SENDING;
spin_unlock_bh(&ar->data_lock);
ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar,
arvif->vdev_id,
bcn->data, bcn->len,
cb->paddr,
cb->bcn.dtim_zero,
cb->bcn.deliver_cab);
spin_lock_bh(&ar->data_lock);
if (ret == 0)
arvif->beacon_state = ATH10K_BEACON_SENT;
else
arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
}
unlock:
spin_unlock_bh(&ar->data_lock);
}
static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
@ -985,12 +1007,10 @@ static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
{
spin_lock_bh(&ar->data_lock);
ieee80211_iterate_active_interfaces_atomic(ar->hw,
IEEE80211_IFACE_ITER_NORMAL,
ath10k_wmi_tx_beacons_iter,
NULL);
spin_unlock_bh(&ar->data_lock);
}
static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
@ -1680,12 +1700,9 @@ int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
struct ath10k_fw_stats_pdev *dst)
void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
struct ath10k_fw_stats_pdev *dst)
{
const struct wal_dbg_tx_stats *tx = &src->wal.tx;
const struct wal_dbg_rx_stats *rx = &src->wal.rx;
dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
@ -1693,44 +1710,63 @@ void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
dst->cycle_count = __le32_to_cpu(src->cycle_count);
dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
}
dst->comp_queued = __le32_to_cpu(tx->comp_queued);
dst->comp_delivered = __le32_to_cpu(tx->comp_delivered);
dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued);
dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued);
dst->wmm_drop = __le32_to_cpu(tx->wmm_drop);
dst->local_enqued = __le32_to_cpu(tx->local_enqued);
dst->local_freed = __le32_to_cpu(tx->local_freed);
dst->hw_queued = __le32_to_cpu(tx->hw_queued);
dst->hw_reaped = __le32_to_cpu(tx->hw_reaped);
dst->underrun = __le32_to_cpu(tx->underrun);
dst->tx_abort = __le32_to_cpu(tx->tx_abort);
dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed);
dst->tx_ko = __le32_to_cpu(tx->tx_ko);
dst->data_rc = __le32_to_cpu(tx->data_rc);
dst->self_triggers = __le32_to_cpu(tx->self_triggers);
dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure);
dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err);
dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry);
dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout);
dst->pdev_resets = __le32_to_cpu(tx->pdev_resets);
dst->phy_underrun = __le32_to_cpu(tx->phy_underrun);
dst->txop_ovf = __le32_to_cpu(tx->txop_ovf);
void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
struct ath10k_fw_stats_pdev *dst)
{
dst->comp_queued = __le32_to_cpu(src->comp_queued);
dst->comp_delivered = __le32_to_cpu(src->comp_delivered);
dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued);
dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued);
dst->wmm_drop = __le32_to_cpu(src->wmm_drop);
dst->local_enqued = __le32_to_cpu(src->local_enqued);
dst->local_freed = __le32_to_cpu(src->local_freed);
dst->hw_queued = __le32_to_cpu(src->hw_queued);
dst->hw_reaped = __le32_to_cpu(src->hw_reaped);
dst->underrun = __le32_to_cpu(src->underrun);
dst->tx_abort = __le32_to_cpu(src->tx_abort);
dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed);
dst->tx_ko = __le32_to_cpu(src->tx_ko);
dst->data_rc = __le32_to_cpu(src->data_rc);
dst->self_triggers = __le32_to_cpu(src->self_triggers);
dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure);
dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err);
dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry);
dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout);
dst->pdev_resets = __le32_to_cpu(src->pdev_resets);
dst->phy_underrun = __le32_to_cpu(src->phy_underrun);
dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
}
dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change);
dst->status_rcvd = __le32_to_cpu(rx->status_rcvd);
dst->r0_frags = __le32_to_cpu(rx->r0_frags);
dst->r1_frags = __le32_to_cpu(rx->r1_frags);
dst->r2_frags = __le32_to_cpu(rx->r2_frags);
dst->r3_frags = __le32_to_cpu(rx->r3_frags);
dst->htt_msdus = __le32_to_cpu(rx->htt_msdus);
dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus);
dst->loc_msdus = __le32_to_cpu(rx->loc_msdus);
dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus);
dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu);
dst->phy_errs = __le32_to_cpu(rx->phy_errs);
dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop);
dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
struct ath10k_fw_stats_pdev *dst)
{
dst->mid_ppdu_route_change = __le32_to_cpu(src->mid_ppdu_route_change);
dst->status_rcvd = __le32_to_cpu(src->status_rcvd);
dst->r0_frags = __le32_to_cpu(src->r0_frags);
dst->r1_frags = __le32_to_cpu(src->r1_frags);
dst->r2_frags = __le32_to_cpu(src->r2_frags);
dst->r3_frags = __le32_to_cpu(src->r3_frags);
dst->htt_msdus = __le32_to_cpu(src->htt_msdus);
dst->htt_mpdus = __le32_to_cpu(src->htt_mpdus);
dst->loc_msdus = __le32_to_cpu(src->loc_msdus);
dst->loc_mpdus = __le32_to_cpu(src->loc_mpdus);
dst->oversize_amsdu = __le32_to_cpu(src->oversize_amsdu);
dst->phy_errs = __le32_to_cpu(src->phy_errs);
dst->phy_err_drop = __le32_to_cpu(src->phy_err_drop);
dst->mpdu_errs = __le32_to_cpu(src->mpdu_errs);
}
void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
struct ath10k_fw_stats_pdev *dst)
{
dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
dst->rts_bad = __le32_to_cpu(src->rts_bad);
dst->rts_good = __le32_to_cpu(src->rts_good);
dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
dst->no_beacons = __le32_to_cpu(src->no_beacons);
dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
}
void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
@ -1768,7 +1804,10 @@ static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
if (!dst)
continue;
ath10k_wmi_pull_pdev_stats(src, dst);
ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
list_add_tail(&dst->list, &stats->pdevs);
}
@ -1820,14 +1859,10 @@ static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar,
if (!dst)
continue;
ath10k_wmi_pull_pdev_stats(&src->old, dst);
dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
dst->rts_bad = __le32_to_cpu(src->rts_bad);
dst->rts_good = __le32_to_cpu(src->rts_good);
dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
dst->no_beacons = __le32_to_cpu(src->no_beacons);
dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
list_add_tail(&dst->list, &stats->pdevs);
}
@ -1856,6 +1891,164 @@ static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar,
return 0;
}
static int ath10k_wmi_10_2_op_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
struct ath10k_fw_stats *stats)
{
const struct wmi_10_2_stats_event *ev = (void *)skb->data;
u32 num_pdev_stats;
u32 num_pdev_ext_stats;
u32 num_vdev_stats;
u32 num_peer_stats;
int i;
if (!skb_pull(skb, sizeof(*ev)))
return -EPROTO;
num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
for (i = 0; i < num_pdev_stats; i++) {
const struct wmi_10_2_pdev_stats *src;
struct ath10k_fw_stats_pdev *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
if (!dst)
continue;
ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
/* FIXME: expose 10.2 specific values */
list_add_tail(&dst->list, &stats->pdevs);
}
for (i = 0; i < num_pdev_ext_stats; i++) {
const struct wmi_10_2_pdev_ext_stats *src;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
/* FIXME: expose values to userspace
*
* Note: Even though this loop seems to do nothing it is
* required to parse following sub-structures properly.
*/
}
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_10_2_peer_stats *src;
struct ath10k_fw_stats_peer *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
if (!dst)
continue;
ath10k_wmi_pull_peer_stats(&src->old, dst);
dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
/* FIXME: expose 10.2 specific values */
list_add_tail(&dst->list, &stats->peers);
}
return 0;
}
static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
struct ath10k_fw_stats *stats)
{
const struct wmi_10_2_stats_event *ev = (void *)skb->data;
u32 num_pdev_stats;
u32 num_pdev_ext_stats;
u32 num_vdev_stats;
u32 num_peer_stats;
int i;
if (!skb_pull(skb, sizeof(*ev)))
return -EPROTO;
num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
for (i = 0; i < num_pdev_stats; i++) {
const struct wmi_10_2_pdev_stats *src;
struct ath10k_fw_stats_pdev *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
if (!dst)
continue;
ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
/* FIXME: expose 10.2 specific values */
list_add_tail(&dst->list, &stats->pdevs);
}
for (i = 0; i < num_pdev_ext_stats; i++) {
const struct wmi_10_2_pdev_ext_stats *src;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
/* FIXME: expose values to userspace
*
* Note: Even though this loop seems to do nothing it is
* required to parse following sub-structures properly.
*/
}
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_10_2_4_peer_stats *src;
struct ath10k_fw_stats_peer *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
if (!dst)
continue;
ath10k_wmi_pull_peer_stats(&src->common.old, dst);
dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate);
/* FIXME: expose 10.2 specific values */
list_add_tail(&dst->list, &stats->peers);
}
return 0;
}
void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb)
{
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
@ -2279,9 +2472,19 @@ void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
spin_lock_bh(&ar->data_lock);
if (arvif->beacon) {
if (!arvif->beacon_sent)
ath10k_warn(ar, "SWBA overrun on vdev %d\n",
switch (arvif->beacon_state) {
case ATH10K_BEACON_SENT:
break;
case ATH10K_BEACON_SCHEDULED:
ath10k_warn(ar, "SWBA overrun on vdev %d, skipped old beacon\n",
arvif->vdev_id);
break;
case ATH10K_BEACON_SENDING:
ath10k_warn(ar, "SWBA overrun on vdev %d, skipped new beacon\n",
arvif->vdev_id);
dev_kfree_skb(bcn);
goto skip;
}
ath10k_mac_vif_beacon_free(arvif);
}
@ -2309,15 +2512,16 @@ void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
}
arvif->beacon = bcn;
arvif->beacon_sent = false;
arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
trace_ath10k_tx_hdr(ar, bcn->data, bcn->len);
trace_ath10k_tx_payload(ar, bcn->data, bcn->len);
ath10k_wmi_tx_beacon_nowait(arvif);
skip:
spin_unlock_bh(&ar->data_lock);
}
ath10k_wmi_tx_beacons_nowait(ar);
}
void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb)
@ -3710,7 +3914,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
struct wmi_init_cmd_10_2 *cmd;
struct sk_buff *buf;
struct wmi_resource_config_10x config = {};
u32 len, val;
u32 len, val, features;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@ -3744,7 +3948,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE);
config.dma_burst_size = __cpu_to_le32(TARGET_10_2_DMA_BURST_SIZE);
config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
@ -3764,6 +3968,9 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd_10_2 *)buf->data;
features = WMI_10_2_RX_BATCH_MODE;
cmd->resource_config.feature_mask = __cpu_to_le32(features);
memcpy(&cmd->resource_config.common, &config, sizeof(config));
ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
@ -4680,12 +4887,12 @@ ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
/* This function assumes the beacon is already DMA mapped */
static struct sk_buff *
ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif)
ath10k_wmi_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id, const void *bcn,
size_t bcn_len, u32 bcn_paddr, bool dtim_zero,
bool deliver_cab)
{
struct ath10k *ar = arvif->ar;
struct wmi_bcn_tx_ref_cmd *cmd;
struct sk_buff *skb;
struct sk_buff *beacon = arvif->beacon;
struct ieee80211_hdr *hdr;
u16 fc;
@ -4693,29 +4900,29 @@ ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif)
if (!skb)
return ERR_PTR(-ENOMEM);
hdr = (struct ieee80211_hdr *)beacon->data;
hdr = (struct ieee80211_hdr *)bcn;
fc = le16_to_cpu(hdr->frame_control);
cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
cmd->data_len = __cpu_to_le32(beacon->len);
cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->data_len = __cpu_to_le32(bcn_len);
cmd->data_ptr = __cpu_to_le32(bcn_paddr);
cmd->msdu_id = 0;
cmd->frame_control = __cpu_to_le32(fc);
cmd->flags = 0;
cmd->antenna_mask = __cpu_to_le32(WMI_BCN_TX_REF_DEF_ANTENNA);
if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
if (dtim_zero)
cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
if (deliver_cab)
cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
return skb;
}
void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
const struct wmi_wmm_params_arg *arg)
void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
const struct wmi_wmm_params_arg *arg)
{
params->cwmin = __cpu_to_le32(arg->cwmin);
params->cwmax = __cpu_to_le32(arg->cwmax);
@ -4727,7 +4934,7 @@ void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
static struct sk_buff *
ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg)
const struct wmi_wmm_params_all_arg *arg)
{
struct wmi_pdev_set_wmm_params *cmd;
struct sk_buff *skb;
@ -4737,10 +4944,10 @@ ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
ath10k_wmi_set_wmm_param(&cmd->ac_be, &arg->ac_be);
ath10k_wmi_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
ath10k_wmi_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
ath10k_wmi_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
return skb;
@ -4784,7 +4991,8 @@ ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar,
}
static struct sk_buff *
ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable)
ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
u32 log_level)
{
struct wmi_dbglog_cfg_cmd *cmd;
struct sk_buff *skb;
@ -4797,7 +5005,7 @@ ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable)
cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
if (module_enable) {
cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE,
cfg = SM(log_level,
ATH10K_DBGLOG_CFG_LOG_LVL);
} else {
/* set back defaults, all modules with WARN level */
@ -4877,6 +5085,109 @@ ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period,
return skb;
}
static struct sk_buff *
ath10k_wmi_op_gen_addba_clear_resp(struct ath10k *ar, u32 vdev_id,
const u8 *mac)
{
struct wmi_addba_clear_resp_cmd *cmd;
struct sk_buff *skb;
if (!mac)
return ERR_PTR(-EINVAL);
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_addba_clear_resp_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(vdev_id);
ether_addr_copy(cmd->peer_macaddr.addr, mac);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi addba clear resp vdev_id 0x%X mac_addr %pM\n",
vdev_id, mac);
return skb;
}
static struct sk_buff *
ath10k_wmi_op_gen_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 buf_size)
{
struct wmi_addba_send_cmd *cmd;
struct sk_buff *skb;
if (!mac)
return ERR_PTR(-EINVAL);
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_addba_send_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(vdev_id);
ether_addr_copy(cmd->peer_macaddr.addr, mac);
cmd->tid = __cpu_to_le32(tid);
cmd->buffersize = __cpu_to_le32(buf_size);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi addba send vdev_id 0x%X mac_addr %pM tid %u bufsize %u\n",
vdev_id, mac, tid, buf_size);
return skb;
}
static struct sk_buff *
ath10k_wmi_op_gen_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 status)
{
struct wmi_addba_setresponse_cmd *cmd;
struct sk_buff *skb;
if (!mac)
return ERR_PTR(-EINVAL);
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_addba_setresponse_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(vdev_id);
ether_addr_copy(cmd->peer_macaddr.addr, mac);
cmd->tid = __cpu_to_le32(tid);
cmd->statuscode = __cpu_to_le32(status);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi addba set resp vdev_id 0x%X mac_addr %pM tid %u status %u\n",
vdev_id, mac, tid, status);
return skb;
}
static struct sk_buff *
ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
u32 tid, u32 initiator, u32 reason)
{
struct wmi_delba_send_cmd *cmd;
struct sk_buff *skb;
if (!mac)
return ERR_PTR(-EINVAL);
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_delba_send_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(vdev_id);
ether_addr_copy(cmd->peer_macaddr.addr, mac);
cmd->tid = __cpu_to_le32(tid);
cmd->initiator = __cpu_to_le32(initiator);
cmd->reasoncode = __cpu_to_le32(reason);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi delba send vdev_id 0x%X mac_addr %pM tid %u initiator %u reason %u\n",
vdev_id, mac, tid, initiator, reason);
return skb;
}
static const struct wmi_ops wmi_ops = {
.rx = ath10k_wmi_op_rx,
.map_svc = wmi_main_svc_map,
@ -4909,6 +5220,7 @@ static const struct wmi_ops wmi_ops = {
.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
/* .gen_vdev_wmm_conf not implemented */
.gen_peer_create = ath10k_wmi_op_gen_peer_create,
.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
@ -4928,6 +5240,13 @@ static const struct wmi_ops wmi_ops = {
.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
/* .gen_pdev_get_temperature not implemented */
.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
.gen_addba_send = ath10k_wmi_op_gen_addba_send,
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
};
static const struct wmi_ops wmi_10_1_ops = {
@ -4965,6 +5284,7 @@ static const struct wmi_ops wmi_10_1_ops = {
.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
/* .gen_vdev_wmm_conf not implemented */
.gen_peer_create = ath10k_wmi_op_gen_peer_create,
.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
@ -4982,10 +5302,18 @@ static const struct wmi_ops wmi_10_1_ops = {
.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
.gen_addba_send = ath10k_wmi_op_gen_addba_send,
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
};
static const struct wmi_ops wmi_10_2_ops = {
.rx = ath10k_wmi_10_2_op_rx,
.pull_fw_stats = ath10k_wmi_10_2_op_pull_fw_stats,
.gen_init = ath10k_wmi_10_2_op_gen_init,
.gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
/* .gen_pdev_get_temperature not implemented */
@ -4993,7 +5321,6 @@ static const struct wmi_ops wmi_10_2_ops = {
/* shared with 10.1 */
.map_svc = wmi_10x_svc_map,
.pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
.pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
.gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
.gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
@ -5020,6 +5347,7 @@ static const struct wmi_ops wmi_10_2_ops = {
.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
/* .gen_vdev_wmm_conf not implemented */
.gen_peer_create = ath10k_wmi_op_gen_peer_create,
.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
@ -5037,10 +5365,15 @@ static const struct wmi_ops wmi_10_2_ops = {
.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
.gen_addba_send = ath10k_wmi_op_gen_addba_send,
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
};
static const struct wmi_ops wmi_10_2_4_ops = {
.rx = ath10k_wmi_10_2_op_rx,
.pull_fw_stats = ath10k_wmi_10_2_4_op_pull_fw_stats,
.gen_init = ath10k_wmi_10_2_op_gen_init,
.gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
.gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
@ -5048,7 +5381,6 @@ static const struct wmi_ops wmi_10_2_4_ops = {
/* shared with 10.1 */
.map_svc = wmi_10x_svc_map,
.pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
.pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
.gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
.gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
@ -5092,6 +5424,13 @@ static const struct wmi_ops wmi_10_2_4_ops = {
.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
.gen_addba_send = ath10k_wmi_op_gen_addba_send,
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
};
int ath10k_wmi_attach(struct ath10k *ar)

View file

@ -551,6 +551,7 @@ struct wmi_cmd_map {
u32 gpio_config_cmdid;
u32 gpio_output_cmdid;
u32 pdev_get_temperature_cmdid;
u32 vdev_set_wmm_params_cmdid;
};
/*
@ -2939,14 +2940,14 @@ struct wmi_wmm_params_arg {
u32 no_ack;
};
struct wmi_pdev_set_wmm_params_arg {
struct wmi_wmm_params_all_arg {
struct wmi_wmm_params_arg ac_be;
struct wmi_wmm_params_arg ac_bk;
struct wmi_wmm_params_arg ac_vi;
struct wmi_wmm_params_arg ac_vo;
};
struct wal_dbg_tx_stats {
struct wmi_pdev_stats_tx {
/* Num HTT cookies queued to dispatch list */
__le32 comp_queued;
@ -3016,7 +3017,7 @@ struct wal_dbg_tx_stats {
__le32 txop_ovf;
} __packed;
struct wal_dbg_rx_stats {
struct wmi_pdev_stats_rx {
/* Cnts any change in ring routing mid-ppdu */
__le32 mid_ppdu_route_change;
@ -3050,17 +3051,11 @@ struct wal_dbg_rx_stats {
__le32 mpdu_errs;
} __packed;
struct wal_dbg_peer_stats {
struct wmi_pdev_stats_peer {
/* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
__le32 dummy;
} __packed;
struct wal_dbg_stats {
struct wal_dbg_tx_stats tx;
struct wal_dbg_rx_stats rx;
struct wal_dbg_peer_stats peer;
} __packed;
enum wmi_stats_id {
WMI_REQUEST_PEER_STAT = 0x01,
WMI_REQUEST_AP_STAT = 0x02
@ -3127,23 +3122,38 @@ struct wmi_stats_event {
u8 data[0];
} __packed;
struct wmi_10_2_stats_event {
__le32 stats_id; /* %WMI_REQUEST_ */
__le32 num_pdev_stats;
__le32 num_pdev_ext_stats;
__le32 num_vdev_stats;
__le32 num_peer_stats;
__le32 num_bcnflt_stats;
u8 data[0];
} __packed;
/*
* PDEV statistics
* TODO: add all PDEV stats here
*/
struct wmi_pdev_stats {
__le32 chan_nf; /* Channel noise floor */
__le32 tx_frame_count; /* TX frame count */
__le32 rx_frame_count; /* RX frame count */
__le32 rx_clear_count; /* rx clear count */
__le32 cycle_count; /* cycle count */
__le32 phy_err_count; /* Phy error count */
__le32 chan_tx_pwr; /* channel tx power */
struct wal_dbg_stats wal; /* WAL dbg stats */
struct wmi_pdev_stats_base {
__le32 chan_nf;
__le32 tx_frame_count;
__le32 rx_frame_count;
__le32 rx_clear_count;
__le32 cycle_count;
__le32 phy_err_count;
__le32 chan_tx_pwr;
} __packed;
struct wmi_10x_pdev_stats {
struct wmi_pdev_stats old;
struct wmi_pdev_stats {
struct wmi_pdev_stats_base base;
struct wmi_pdev_stats_tx tx;
struct wmi_pdev_stats_rx rx;
struct wmi_pdev_stats_peer peer;
} __packed;
struct wmi_pdev_stats_extra {
__le32 ack_rx_bad;
__le32 rts_bad;
__le32 rts_good;
@ -3152,6 +3162,30 @@ struct wmi_10x_pdev_stats {
__le32 mib_int_count;
} __packed;
struct wmi_10x_pdev_stats {
struct wmi_pdev_stats_base base;
struct wmi_pdev_stats_tx tx;
struct wmi_pdev_stats_rx rx;
struct wmi_pdev_stats_peer peer;
struct wmi_pdev_stats_extra extra;
} __packed;
struct wmi_pdev_stats_mem {
__le32 dram_free;
__le32 iram_free;
} __packed;
struct wmi_10_2_pdev_stats {
struct wmi_pdev_stats_base base;
struct wmi_pdev_stats_tx tx;
__le32 mc_drop;
struct wmi_pdev_stats_rx rx;
__le32 pdev_rx_timeout;
struct wmi_pdev_stats_mem mem;
struct wmi_pdev_stats_peer peer;
struct wmi_pdev_stats_extra extra;
} __packed;
/*
* VDEV statistics
* TODO: add all VDEV stats here
@ -3175,6 +3209,32 @@ struct wmi_10x_peer_stats {
__le32 peer_rx_rate;
} __packed;
struct wmi_10_2_peer_stats {
struct wmi_peer_stats old;
__le32 peer_rx_rate;
__le32 current_per;
__le32 retries;
__le32 tx_rate_count;
__le32 max_4ms_frame_len;
__le32 total_sub_frames;
__le32 tx_bytes;
__le32 num_pkt_loss_overflow[4];
__le32 num_pkt_loss_excess_retry[4];
} __packed;
struct wmi_10_2_4_peer_stats {
struct wmi_10_2_peer_stats common;
__le32 unknown_value; /* FIXME: what is this word? */
} __packed;
struct wmi_10_2_pdev_ext_stats {
__le32 rx_rssi_comb;
__le32 rx_rssi[4];
__le32 rx_mcs[10];
__le32 tx_mcs[10];
__le32 ack_rssi;
} __packed;
struct wmi_vdev_create_cmd {
__le32 vdev_id;
__le32 vdev_type;
@ -4060,6 +4120,30 @@ enum wmi_sta_ps_param_uapsd {
WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7),
};
#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX
struct wmi_sta_uapsd_auto_trig_param {
__le32 wmm_ac;
__le32 user_priority;
__le32 service_interval;
__le32 suspend_interval;
__le32 delay_interval;
};
struct wmi_sta_uapsd_auto_trig_cmd_fixed_param {
__le32 vdev_id;
struct wmi_mac_addr peer_macaddr;
__le32 num_ac;
};
struct wmi_sta_uapsd_auto_trig_arg {
u32 wmm_ac;
u32 user_priority;
u32 service_interval;
u32 suspend_interval;
u32 delay_interval;
};
enum wmi_sta_powersave_param {
/*
* Controls how frames are retrievd from AP while STA is sleeping
@ -4430,7 +4514,7 @@ struct wmi_peer_set_q_empty_callback_cmd {
#define WMI_PEER_SPATIAL_MUX 0x00200000
#define WMI_PEER_VHT 0x02000000
#define WMI_PEER_80MHZ 0x04000000
#define WMI_PEER_PMF 0x08000000
#define WMI_PEER_VHT_2G 0x08000000
/*
* Peer rate capabilities.
@ -4581,6 +4665,11 @@ enum wmi_sta_keepalive_method {
WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
};
#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
/* Firmware crashes if keepalive interval exceeds this limit */
#define WMI_STA_KEEPALIVE_INTERVAL_MAX_SECONDS 0xffff
/* note: ip4 addresses are in network byte order, i.e. big endian */
struct wmi_sta_keepalive_arp_resp {
__be32 src_ip4_addr;
@ -4596,6 +4685,16 @@ struct wmi_sta_keepalive_cmd {
struct wmi_sta_keepalive_arp_resp arp_resp;
} __packed;
struct wmi_sta_keepalive_arg {
u32 vdev_id;
u32 enabled;
u32 method;
u32 interval;
__be32 src_ip4_addr;
__be32 dest_ip4_addr;
const u8 dest_mac_addr[ETH_ALEN];
};
enum wmi_force_fw_hang_type {
WMI_FORCE_FW_HANG_ASSERT = 1,
WMI_FORCE_FW_HANG_NO_DETECT,
@ -4772,16 +4871,22 @@ int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
u32 cmd_id);
void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
struct ath10k_fw_stats_pdev *dst);
void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
struct ath10k_fw_stats_pdev *dst);
void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
struct ath10k_fw_stats_pdev *dst);
void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
struct ath10k_fw_stats_pdev *dst);
void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
struct ath10k_fw_stats_pdev *dst);
void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
struct ath10k_fw_stats_peer *dst);
void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
struct wmi_host_mem_chunks *chunks);
void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
const struct wmi_start_scan_arg *arg);
void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
const struct wmi_wmm_params_arg *arg);
void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
const struct wmi_wmm_params_arg *arg);
void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
const struct wmi_channel_arg *arg);
int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg);

View file

@ -478,7 +478,7 @@ ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
regval = ioread32(reg);
iowrite32(regval | val, reg);
regval = ioread32(reg);
usleep_range(100, 150);
udelay(100); /* NB: should be atomic */
/* Bring BB/MAC out of reset */
iowrite32(regval & ~val, reg);

View file

@ -1203,24 +1203,41 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah)
static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
{
int offset[8] = {0}, total = 0, test;
int agc_out, i;
int agc_out, i, peak_detect_threshold;
if (AR_SREV_9550(ah) || AR_SREV_9531(ah))
peak_detect_threshold = 8;
else
peak_detect_threshold = 0;
/*
* Turn off LNA/SW.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1);
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0);
if (is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0);
else
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0);
if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9330_11(ah)) {
if (is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0);
else
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0);
}
/*
* Turn off RXON.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
AR_PHY_65NM_RXTX2_RXON_OVR, 0x1);
REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
AR_PHY_65NM_RXTX2_RXON, 0x0);
/*
* Turn on AGC for cal.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
@ -1228,16 +1245,19 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1);
if (AR_SREV_9330_11(ah)) {
if (AR_SREV_9330_11(ah))
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0);
} else {
if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
if (is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0);
AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
peak_detect_threshold);
else
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0);
AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR,
peak_detect_threshold);
}
for (i = 6; i > 0; i--) {
@ -1266,10 +1286,19 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total);
/*
* Turn on LNA.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0);
/*
* Turn off RXON.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
AR_PHY_65NM_RXTX2_RXON_OVR, 0);
/*
* Turn off peak detect calibration.
*/
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0);
}
@ -1611,8 +1640,14 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
skip_tx_iqcal:
if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
if (AR_SREV_9330_11(ah))
ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));
if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (!(ah->rxchainmask & (1 << i)))
continue;
ar9003_hw_manual_peak_cal(ah, i,
IS_CHAN_2GHZ(chan));
}
}
/*
* For non-AR9550 chips, we just trigger AGC calibration

View file

@ -17,23 +17,9 @@
#include <linux/export.h>
#include "ath9k.h"
#include "reg.h"
#include "reg_wow.h"
#include "hw-ops.h"
const char *ath9k_hw_wow_event_to_string(u32 wow_event)
{
if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
return "Magic pattern";
if (wow_event & AH_WOW_USER_PATTERN_EN)
return "User pattern";
if (wow_event & AH_WOW_LINK_CHANGE)
return "Link change";
if (wow_event & AH_WOW_BEACON_MISS)
return "Beacon miss";
return "unknown reason";
}
EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
@ -49,6 +35,15 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
return;
}
if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
if (!REG_READ(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL))
REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
} else if (AR_SREV_9485(ah)){
if (!(REG_READ(ah, AR_NDP2_TIMER_MODE) &
AR_GEN_TIMERS2_MODE_ENABLE_MASK))
REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
}
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
}
@ -67,11 +62,15 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
/* set the transmit buffer */
ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
ctl[1] = 0;
ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */
ctl[4] = 0;
ctl[7] = (ah->txchainmask) << 2;
ctl[2] = 0xf << 16; /* tx_tries 0 */
if (IS_CHAN_2GHZ(ah->curchan))
ctl[3] = 0x1b; /* CCK_1M */
else
ctl[3] = 0xb; /* OFDM_6M */
for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
@ -103,21 +102,22 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
}
void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
u8 *user_mask, int pattern_count,
int pattern_len)
int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
u8 *user_mask, int pattern_count,
int pattern_len)
{
int i;
u32 pattern_val, mask_val;
u32 set, clr;
/* FIXME: should check count by querying the hardware capability */
if (pattern_count >= MAX_NUM_PATTERN)
return;
if (pattern_count >= ah->wow.max_patterns)
return -ENOSPC;
REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
if (pattern_count < MAX_NUM_PATTERN_LEGACY)
REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
else
REG_SET_BIT(ah, AR_MAC_PCU_WOW4, BIT(pattern_count - 8));
/* set the registers for pattern */
for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
memcpy(&pattern_val, user_pattern, 4);
REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i),
@ -125,49 +125,42 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
user_pattern += 4;
}
/* set the registers for mask */
for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
memcpy(&mask_val, user_mask, 4);
REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val);
user_mask += 4;
}
/* set the pattern length to be matched
*
* AR_WOW_LENGTH1_REG1
* bit 31:24 pattern 0 length
* bit 23:16 pattern 1 length
* bit 15:8 pattern 2 length
* bit 7:0 pattern 3 length
*
* AR_WOW_LENGTH1_REG2
* bit 31:24 pattern 4 length
* bit 23:16 pattern 5 length
* bit 15:8 pattern 6 length
* bit 7:0 pattern 7 length
*
* the below logic writes out the new
* pattern length for the corresponding
* pattern_count, while masking out the
* other fields
*/
ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
if (pattern_count < MAX_NUM_PATTERN_LEGACY)
ah->wow.wow_event_mask |=
BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
else
ah->wow.wow_event_mask2 |=
BIT((pattern_count - 8) + AR_WOW_PAT_FOUND_SHIFT);
if (pattern_count < 4) {
/* Pattern 0-3 uses AR_WOW_LENGTH1 register */
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
AR_WOW_LEN1_SHIFT(pattern_count);
clr = AR_WOW_LENGTH1_MASK(pattern_count);
REG_RMW(ah, AR_WOW_LENGTH1, set, clr);
} else {
/* Pattern 4-7 uses AR_WOW_LENGTH2 register */
} else if (pattern_count < 8) {
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
AR_WOW_LEN2_SHIFT(pattern_count);
clr = AR_WOW_LENGTH2_MASK(pattern_count);
REG_RMW(ah, AR_WOW_LENGTH2, set, clr);
} else if (pattern_count < 12) {
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
AR_WOW_LEN3_SHIFT(pattern_count);
clr = AR_WOW_LENGTH3_MASK(pattern_count);
REG_RMW(ah, AR_WOW_LENGTH3, set, clr);
} else if (pattern_count < MAX_NUM_PATTERN) {
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
AR_WOW_LEN4_SHIFT(pattern_count);
clr = AR_WOW_LENGTH4_MASK(pattern_count);
REG_RMW(ah, AR_WOW_LENGTH4, set, clr);
}
return 0;
}
EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern);
@ -189,7 +182,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
* register. This mask will clean it up.
*/
val &= ah->wow_event_mask;
val &= ah->wow.wow_event_mask;
if (val) {
if (val & AR_WOW_MAGIC_PAT_FOUND)
@ -233,190 +226,192 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
if (ah->is_pciexpress)
ath9k_hw_configpcipowersave(ah, false);
ah->wow_event_mask = 0;
ah->wow.wow_event_mask = 0;
return wow_status;
}
EXPORT_SYMBOL(ath9k_hw_wow_wakeup);
static void ath9k_hw_wow_set_arwr_reg(struct ath_hw *ah)
{
u32 wa_reg;
if (!ah->is_pciexpress)
return;
/*
* We need to untie the internal POR (power-on-reset)
* to the external PCI-E reset. We also need to tie
* the PCI-E Phy reset to the PCI-E reset.
*/
wa_reg = REG_READ(ah, AR_WA);
wa_reg &= ~AR_WA_UNTIE_RESET_EN;
wa_reg |= AR_WA_RESET_EN;
wa_reg |= AR_WA_POR_SHORT;
REG_WRITE(ah, AR_WA, wa_reg);
}
void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
{
u32 wow_event_mask;
u32 set, clr;
u32 keep_alive, magic_pattern, host_pm_ctrl;
wow_event_mask = ah->wow.wow_event_mask;
/*
* wow_event_mask is a mask to the AR_WOW_PATTERN register to
* indicate which WoW events we have enabled. The WoW events
* are from the 'pattern_enable' in this function and
* 'pattern_count' of ath9k_hw_wow_apply_pattern()
*/
wow_event_mask = ah->wow_event_mask;
/*
* Untie Power-on-Reset from the PCI-E-Reset. When we are in
* WOW sleep, we do want the Reset from the PCI-E to disturb
* our hw state
*/
if (ah->is_pciexpress) {
/*
* we need to untie the internal POR (power-on-reset)
* to the external PCI-E reset. We also need to tie
* the PCI-E Phy reset to the PCI-E reset.
*/
set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
REG_RMW(ah, AR_WA, set, clr);
}
/*
* set the power states appropriately and enable PME
*/
set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA |
AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR;
/*
* set and clear WOW_PME_CLEAR registers for the chip
* AR_PMCTRL_HOST_PME_EN - Override PME enable in configuration
* space and allow MAC to generate WoW anyway.
*
* AR_PMCTRL_PWR_PM_CTRL_ENA - ???
*
* AR_PMCTRL_AUX_PWR_DET - PCI core SYS_AUX_PWR_DET signal,
* needs to be set for WoW in PCI mode.
*
* AR_PMCTRL_WOW_PME_CLR - WoW Clear Signal going to the MAC.
*
* Set the power states appropriately and enable PME.
*
* Set and clear WOW_PME_CLEAR for the chip
* to generate next wow signal.
*/
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
clr = AR_PMCTRL_WOW_PME_CLR;
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_HOST_PME_EN |
AR_PMCTRL_PWR_PM_CTRL_ENA |
AR_PMCTRL_AUX_PWR_DET |
AR_PMCTRL_WOW_PME_CLR);
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR);
/*
* Setup for:
* - beacon misses
* - magic pattern
* - keep alive timeout
* - pattern matching
* Random Backoff.
*
* 31:28 in AR_WOW_PATTERN : Indicates the number of bits used in the
* contention window. For value N,
* the random backoff will be selected between
* 0 and (2 ^ N) - 1.
*/
REG_SET_BIT(ah, AR_WOW_PATTERN,
AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF));
/*
* Program default values for pattern backoff, aifs/slot/KAL count,
* beacon miss timeout, KAL timeout, etc.
* AIFS time, Slot time, Keep Alive count.
*/
REG_SET_BIT(ah, AR_WOW_COUNT, AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT));
/*
* Beacon timeout.
*/
set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
REG_SET_BIT(ah, AR_WOW_PATTERN, set);
set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
REG_SET_BIT(ah, AR_WOW_COUNT, set);
if (pattern_enable & AH_WOW_BEACON_MISS)
set = AR_WOW_BEACON_TIMO;
/* We are not using beacon miss, program a large value */
REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO);
else
set = AR_WOW_BEACON_TIMO_MAX;
REG_WRITE(ah, AR_WOW_BCN_TIMO, set);
REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO_MAX);
/*
* Keep alive timo in ms except AR9280
* Keep alive timeout in ms.
*/
if (!pattern_enable)
set = AR_WOW_KEEP_ALIVE_NEVER;
REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, AR_WOW_KEEP_ALIVE_NEVER);
else
set = KAL_TIMEOUT * 32;
REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set);
REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, KAL_TIMEOUT * 32);
/*
* Keep alive delay in us. based on 'power on clock',
* therefore in usec
* Keep alive delay in us.
*/
set = KAL_DELAY * 1000;
REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set);
REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, KAL_DELAY * 1000);
/*
* Create keep alive pattern to respond to beacons
* Create keep alive pattern to respond to beacons.
*/
ath9k_wow_create_keep_alive_pattern(ah);
/*
* Configure MAC WoW Registers
* Configure keep alive register.
*/
set = 0;
keep_alive = REG_READ(ah, AR_WOW_KEEP_ALIVE);
/* Send keep alive timeouts anyway */
clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
keep_alive &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
if (pattern_enable & AH_WOW_LINK_CHANGE)
if (pattern_enable & AH_WOW_LINK_CHANGE) {
keep_alive &= ~AR_WOW_KEEP_ALIVE_FAIL_DIS;
wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
else
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
} else {
keep_alive |= AR_WOW_KEEP_ALIVE_FAIL_DIS;
}
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
REG_WRITE(ah, AR_WOW_KEEP_ALIVE, keep_alive);
/*
* we are relying on a bmiss failure. ensure we have
* enough threshold to prevent false positives
* We are relying on a bmiss failure, ensure we have
* enough threshold to prevent false positives.
*/
REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
AR_WOW_BMISSTHRESHOLD);
set = 0;
clr = 0;
if (pattern_enable & AH_WOW_BEACON_MISS) {
set = AR_WOW_BEACON_FAIL_EN;
wow_event_mask |= AR_WOW_BEACON_FAIL;
REG_SET_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
} else {
clr = AR_WOW_BEACON_FAIL_EN;
REG_CLR_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
}
REG_RMW(ah, AR_WOW_BCN_EN, set, clr);
set = 0;
clr = 0;
/*
* Enable the magic packet registers
* Enable the magic packet registers.
*/
magic_pattern = REG_READ(ah, AR_WOW_PATTERN);
magic_pattern |= AR_WOW_MAC_INTR_EN;
if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) {
set = AR_WOW_MAGIC_EN;
magic_pattern |= AR_WOW_MAGIC_EN;
wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
} else {
clr = AR_WOW_MAGIC_EN;
magic_pattern &= ~AR_WOW_MAGIC_EN;
}
set |= AR_WOW_MAC_INTR_EN;
REG_RMW(ah, AR_WOW_PATTERN, set, clr);
REG_WRITE(ah, AR_WOW_PATTERN, magic_pattern);
/*
* Enable pattern matching for packets which are less
* than 256 bytes.
*/
REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
AR_WOW_PATTERN_SUPPORTED);
/*
* Set the power states appropriately and enable PME
* Set the power states appropriately and enable PME.
*/
clr = 0;
set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
AR_PMCTRL_PWR_PM_CTRL_ENA;
host_pm_ctrl = REG_READ(ah, AR_PCIE_PM_CTRL);
host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3 |
AR_PMCTRL_HOST_PME_EN |
AR_PMCTRL_PWR_PM_CTRL_ENA;
host_pm_ctrl &= ~AR_PCIE_PM_CTRL_ENA;
clr = AR_PCIE_PM_CTRL_ENA;
REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
if (AR_SREV_9462(ah)) {
/*
* This is needed to prevent the chip waking up
* the host within 3-4 seconds with certain
* platform/BIOS.
*/
host_pm_ctrl &= ~AR_PMCTRL_PWR_STATE_D1D3;
host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3_REAL;
}
REG_WRITE(ah, AR_PCIE_PM_CTRL, host_pm_ctrl);
/*
* this is needed to prevent the chip waking up
* the host within 3-4 seconds with certain
* platform/BIOS. The fix is to enable
* D1 & D3 to match original definition and
* also match the OTP value. Anyway this
* is more related to SW WOW.
* Enable sequence number generation when asleep.
*/
clr = AR_PMCTRL_PWR_STATE_D1D3;
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
/* to bring down WOW power low margin */
set = BIT(13);
REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
/* To bring down WOW power low margin */
REG_SET_BIT(ah, AR_PCIE_PHY_REG3, BIT(13));
ath9k_hw_wow_set_arwr_reg(ah);
/* HW WoW */
clr = BIT(5);
REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, BIT(5));
ath9k_hw_set_powermode_wow_sleep(ah);
ah->wow_event_mask = wow_event_mask;
ah->wow.wow_event_mask = wow_event_mask;
}
EXPORT_SYMBOL(ath9k_hw_wow_enable);

View file

@ -358,7 +358,7 @@ static const u32 qca953x_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -378,7 +378,7 @@ static const u32 qca953x_1p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},

View file

@ -63,7 +63,7 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -83,7 +83,7 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},

View file

@ -34,7 +34,7 @@ struct ath_vif;
extern struct ieee80211_ops ath9k_ops;
extern int ath9k_modparam_nohwcrypt;
extern int led_blink;
extern int ath9k_led_blink;
extern bool is_ath9k_unloaded;
extern int ath9k_use_chanctx;
@ -830,14 +830,9 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
/* Wake on Wireless LAN */
/************************/
struct ath9k_wow_pattern {
u8 pattern_bytes[MAX_PATTERN_SIZE];
u8 mask_bytes[MAX_PATTERN_SIZE];
u32 pattern_len;
};
#ifdef CONFIG_ATH9K_WOW
void ath9k_init_wow(struct ieee80211_hw *hw);
void ath9k_deinit_wow(struct ieee80211_hw *hw);
int ath9k_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
int ath9k_resume(struct ieee80211_hw *hw);
@ -846,6 +841,9 @@ void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled);
static inline void ath9k_init_wow(struct ieee80211_hw *hw)
{
}
static inline void ath9k_deinit_wow(struct ieee80211_hw *hw)
{
}
static inline int ath9k_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
@ -1039,9 +1037,8 @@ struct ath_softc {
s16 tx99_power;
#ifdef CONFIG_ATH9K_WOW
atomic_t wow_got_bmiss_intr;
atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */
u32 wow_intr_before_sleep;
bool force_wow;
#endif
};

View file

@ -582,7 +582,7 @@ static struct rchan_callbacks rfs_spec_scan_cb = {
void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
{
if (config_enabled(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) {
if (config_enabled(CONFIG_ATH9K_DEBUGFS)) {
relay_close(spec_priv->rfs_chan_spec_scan);
spec_priv->rfs_chan_spec_scan = NULL;
}

View file

@ -1043,6 +1043,69 @@ static const struct file_operations fops_ackto = {
};
#endif
#ifdef CONFIG_ATH9K_WOW
static ssize_t read_file_wow(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
unsigned int len = 0, size = 32;
ssize_t retval;
char *buf;
buf = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
len += scnprintf(buf + len, size - len, "WOW: %s\n",
sc->force_wow ? "ENABLED" : "DISABLED");
if (len > size)
len = size;
retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return retval;
}
static ssize_t write_file_wow(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val != 1)
return -EINVAL;
if (!sc->force_wow) {
sc->force_wow = true;
ath9k_init_wow(sc->hw);
}
return count;
}
static const struct file_operations fops_wow = {
.read = read_file_wow,
.write = write_file_wow,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#endif
static ssize_t read_file_tpc(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -1313,6 +1376,11 @@ int ath9k_init_debug(struct ath_hw *ah)
&fops_btcoex);
#endif
#ifdef CONFIG_ATH9K_WOW
debugfs_create_file("wow", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_wow);
#endif
#ifdef CONFIG_ATH9K_DYNACK
debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_ackto);

View file

@ -49,7 +49,7 @@ void ath_init_leds(struct ath_softc *sc)
if (AR_SREV_9100(sc->sc_ah))
return;
if (!led_blink)
if (!ath9k_led_blink)
sc->led_cdev.default_trigger =
ieee80211_get_radio_led_name(sc->hw);

View file

@ -45,7 +45,7 @@
extern struct ieee80211_ops ath9k_htc_ops;
extern int htc_modparam_nohwcrypt;
#ifdef CONFIG_MAC80211_LEDS
extern int led_blink;
extern int ath9k_htc_led_blink;
#endif
enum htc_phymode {

View file

@ -279,7 +279,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv)
else
priv->ah->led_pin = ATH_LED_PIN_DEF;
if (!led_blink)
if (!ath9k_htc_led_blink)
priv->led_cdev.default_trigger =
ieee80211_get_radio_led_name(priv->hw);

View file

@ -39,8 +39,8 @@ module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
#ifdef CONFIG_MAC80211_LEDS
int led_blink = 1;
module_param_named(blink, led_blink, int, 0444);
int ath9k_htc_led_blink = 1;
module_param_named(blink, ath9k_htc_led_blink, int, 0444);
MODULE_PARM_DESC(blink, "Enable LED blink on activity");
static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = {

View file

@ -2544,13 +2544,17 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->hw_caps |= ATH9K_HW_CAP_RTT;
}
if (AR_SREV_9462(ah))
pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
if (AR_SREV_9300_20_OR_LATER(ah) &&
ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
#ifdef CONFIG_ATH9K_WOW
if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
ah->wow.max_patterns = MAX_NUM_PATTERN;
else
ah->wow.max_patterns = MAX_NUM_PATTERN_LEGACY;
#endif
return 0;
}

View file

@ -199,12 +199,13 @@
#define KAL_NUM_DESC_WORDS 12
#define KAL_ANTENNA_MODE 1
#define KAL_TO_DS 1
#define KAL_DELAY 4 /*delay of 4ms between 2 KAL frames */
#define KAL_DELAY 4 /* delay of 4ms between 2 KAL frames */
#define KAL_TIMEOUT 900
#define MAX_PATTERN_SIZE 256
#define MAX_PATTERN_MASK_SIZE 32
#define MAX_NUM_PATTERN 8
#define MAX_NUM_PATTERN 16
#define MAX_NUM_PATTERN_LEGACY 8
#define MAX_NUM_USER_PATTERN 6 /* deducting the disassociate and
deauthenticate packets */
@ -248,12 +249,10 @@ enum ath9k_hw_caps {
#ifdef CONFIG_ATH9K_PCOEM
ATH9K_HW_CAP_RTT = BIT(14),
ATH9K_HW_CAP_MCI = BIT(15),
ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(16),
ATH9K_HW_CAP_BT_ANT_DIV = BIT(17),
#else
ATH9K_HW_CAP_RTT = 0,
ATH9K_HW_CAP_MCI = 0,
ATH9K_HW_WOW_DEVICE_CAPABLE = 0,
ATH9K_HW_CAP_BT_ANT_DIV = 0,
#endif
ATH9K_HW_CAP_DFS = BIT(18),
@ -272,6 +271,12 @@ enum ath9k_hw_caps {
* of those types.
*/
struct ath9k_hw_wow {
u32 wow_event_mask;
u32 wow_event_mask2;
u8 max_patterns;
};
struct ath9k_hw_capabilities {
u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */
u16 rts_aggr_limit;
@ -930,7 +935,7 @@ struct ath_hw {
u32 ent_mode;
#ifdef CONFIG_ATH9K_WOW
u32 wow_event_mask;
struct ath9k_hw_wow wow;
#endif
bool is_clk_25mhz;
int (*get_mac_revision)(void);
@ -1148,23 +1153,19 @@ ath9k_hw_get_btcoex_scheme(struct ath_hw *ah)
#ifdef CONFIG_ATH9K_WOW
const char *ath9k_hw_wow_event_to_string(u32 wow_event);
void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
u8 *user_mask, int pattern_count,
int pattern_len);
int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
u8 *user_mask, int pattern_count,
int pattern_len);
u32 ath9k_hw_wow_wakeup(struct ath_hw *ah);
void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable);
#else
static inline const char *ath9k_hw_wow_event_to_string(u32 wow_event)
{
return NULL;
}
static inline void ath9k_hw_wow_apply_pattern(struct ath_hw *ah,
u8 *user_pattern,
u8 *user_mask,
int pattern_count,
int pattern_len)
static inline int ath9k_hw_wow_apply_pattern(struct ath_hw *ah,
u8 *user_pattern,
u8 *user_mask,
int pattern_count,
int pattern_len)
{
return 0;
}
static inline u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
{

View file

@ -45,8 +45,8 @@ int ath9k_modparam_nohwcrypt;
module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
int led_blink;
module_param_named(blink, led_blink, int, 0444);
int ath9k_led_blink;
module_param_named(blink, ath9k_led_blink, int, 0444);
MODULE_PARM_DESC(blink, "Enable LED blink on activity");
static int ath9k_btcoex_enable;
@ -996,6 +996,7 @@ void ath9k_deinit_device(struct ath_softc *sc)
ath9k_ps_restore(sc);
ath9k_deinit_debug(sc);
ath9k_deinit_wow(hw);
ieee80211_unregister_hw(hw);
ath_rx_cleanup(sc);
ath9k_deinit_softc(sc);

View file

@ -555,15 +555,6 @@ irqreturn_t ath_isr(int irq, void *dev)
(status & ATH9K_INT_BB_WATCHDOG))
goto chip_reset;
#ifdef CONFIG_ATH9K_WOW
if (status & ATH9K_INT_BMISS) {
if (atomic_read(&sc->wow_sleep_proc_intr) == 0) {
atomic_inc(&sc->wow_got_bmiss_intr);
atomic_dec(&sc->wow_sleep_proc_intr);
}
}
#endif
if (status & ATH9K_INT_SWBA)
tasklet_schedule(&sc->bcon_tasklet);

View file

@ -998,9 +998,12 @@ static int ath_pci_suspend(struct device *device)
struct pci_dev *pdev = to_pci_dev(device);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
if (sc->wow_enabled)
if (test_bit(ATH_OP_WOW_ENABLED, &common->op_flags)) {
dev_info(&pdev->dev, "WOW is enabled, bypassing PCI suspend\n");
return 0;
}
/* The device has to be moved to FULLSLEEP forcibly.
* Otherwise the chip never moved to full sleep,

View file

@ -900,10 +900,13 @@
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
#define AR_SREV_9565(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
#define AR_SREV_9003_PCOEM(_ah) \
(AR_SREV_9462(_ah) || AR_SREV_9485(_ah) || AR_SREV_9565(_ah))
#else
#define AR_SREV_9462(_ah) 0
#define AR_SREV_9485(_ah) 0
#define AR_SREV_9565(_ah) 0
#define AR_SREV_9003_PCOEM(_ah) 0
#endif
#define AR_SREV_9485_11_OR_LATER(_ah) \
@ -1880,6 +1883,7 @@ enum {
#define AR_FIRST_NDP_TIMER 7
#define AR_NDP2_PERIOD 0x81a0
#define AR_NDP2_TIMER_MODE 0x81c0
#define AR_GEN_TIMERS2_MODE_ENABLE_MASK 0x000000FF
#define AR_GEN_TIMERS(_i) (0x8200 + ((_i) << 2))
#define AR_NEXT_TBTT_TIMER AR_GEN_TIMERS(0)
@ -1975,6 +1979,7 @@ enum {
#define AR_DIRECT_CONNECT 0x83a0
#define AR_DC_AP_STA_EN 0x00000001
#define AR_DC_TSF2_ENABLE 0x00000001
#define AR_AES_MUTE_MASK0 0x805c
#define AR_AES_MUTE_MASK0_FC 0x0000FFFF
@ -2007,126 +2012,6 @@ enum {
#define AR_WOW_BEACON_TIMO_MAX 0xffffffff
/*
* MAC WoW Registers
*/
#define AR_WOW_PATTERN 0x825C
#define AR_WOW_COUNT 0x8260
#define AR_WOW_BCN_EN 0x8270
#define AR_WOW_BCN_TIMO 0x8274
#define AR_WOW_KEEP_ALIVE_TIMO 0x8278
#define AR_WOW_KEEP_ALIVE 0x827c
#define AR_WOW_US_SCALAR 0x8284
#define AR_WOW_KEEP_ALIVE_DELAY 0x8288
#define AR_WOW_PATTERN_MATCH 0x828c
#define AR_WOW_PATTERN_OFF1 0x8290 /* pattern bytes 0 -> 3 */
#define AR_WOW_PATTERN_OFF2 0x8294 /* pattern bytes 4 -> 7 */
/* for AR9285 or later version of chips */
#define AR_WOW_EXACT 0x829c
#define AR_WOW_LENGTH1 0x8360
#define AR_WOW_LENGTH2 0X8364
/* register to enable match for less than 256 bytes packets */
#define AR_WOW_PATTERN_MATCH_LT_256B 0x8368
#define AR_SW_WOW_CONTROL 0x20018
#define AR_SW_WOW_ENABLE 0x1
#define AR_SWITCH_TO_REFCLK 0x2
#define AR_RESET_CONTROL 0x4
#define AR_RESET_VALUE_MASK 0x8
#define AR_HW_WOW_DISABLE 0x10
#define AR_CLR_MAC_INTERRUPT 0x20
#define AR_CLR_KA_INTERRUPT 0x40
/* AR_WOW_PATTERN register values */
#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 28) /* in usecs */
#define AR_WOW_MAC_INTR_EN 0x00040000
#define AR_WOW_MAGIC_EN 0x00010000
#define AR_WOW_PATTERN_EN(x) (x & 0xff)
#define AR_WOW_PAT_FOUND_SHIFT 8
#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PAT_FOUND_SHIFT))
#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PAT_FOUND_SHIFT)
#define AR_WOW_MAGIC_PAT_FOUND 0x00020000
#define AR_WOW_MAC_INTR 0x00080000
#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000
#define AR_WOW_BEACON_FAIL 0x00200000
#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \
AR_WOW_MAGIC_PAT_FOUND | \
AR_WOW_KEEP_ALIVE_FAIL | \
AR_WOW_BEACON_FAIL))
#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \
AR_WOW_MAGIC_EN | \
AR_WOW_MAC_INTR_EN | \
AR_WOW_BEACON_FAIL | \
AR_WOW_KEEP_ALIVE_FAIL))
/* AR_WOW_COUNT register values */
#define AR_WOW_AIFS_CNT(x) (x & 0xff)
#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8)
#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16)
/* AR_WOW_BCN_EN register */
#define AR_WOW_BEACON_FAIL_EN 0x00000001
/* AR_WOW_BCN_TIMO rgister */
#define AR_WOW_BEACON_TIMO 0x40000000 /* valid if BCN_EN is set */
/* AR_WOW_KEEP_ALIVE_TIMO register */
#define AR_WOW_KEEP_ALIVE_TIMO_VALUE
#define AR_WOW_KEEP_ALIVE_NEVER 0xffffffff
/* AR_WOW_KEEP_ALIVE register */
#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001
#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002
/* AR_WOW_KEEP_ALIVE_DELAY register */
#define AR_WOW_KEEP_ALIVE_DELAY_VALUE 0x000003e8 /* 1 msec */
/*
* keep it long for beacon workaround - ensure no false alarm
*/
#define AR_WOW_BMISSTHRESHOLD 0x20
/* AR_WOW_PATTERN_MATCH register */
#define AR_WOW_PAT_END_OF_PKT(x) (x & 0xf)
#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8)
/*
* default values for Wow Configuration for backoff, aifs, slot, keep-alive
* to be programmed into various registers.
*/
#define AR_WOW_PAT_BACKOFF 0x00000004 /* AR_WOW_PATTERN_REG */
#define AR_WOW_CNT_AIFS_CNT 0x00000022 /* AR_WOW_COUNT_REG */
#define AR_WOW_CNT_SLOT_CNT 0x00000009 /* AR_WOW_COUNT_REG */
/*
* Keepalive count applicable for AR9280 2.0 and above.
*/
#define AR_WOW_CNT_KA_CNT 0x00000008 /* AR_WOW_COUNT register */
/* WoW - Transmit buffer for keep alive frames */
#define AR_WOW_TRANSMIT_BUFFER 0xe000 /* E000 - EFFC */
#define AR_WOW_TXBUF(i) (AR_WOW_TRANSMIT_BUFFER + ((i) << 2))
#define AR_WOW_KA_DESC_WORD2 0xe000
#define AR_WOW_KA_DATA_WORD0 0xe030
/* WoW Transmit Buffer for patterns */
#define AR_WOW_TB_PATTERN(i) (0xe100 + (i << 8))
#define AR_WOW_TB_MASK(i) (0xec00 + (i << 5))
/* Currently Pattern 0-7 are supported - so bit 0-7 are set */
#define AR_WOW_PATTERN_SUPPORTED 0xff
#define AR_WOW_LENGTH_MAX 0xff
#define AR_WOW_LEN1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3)
#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i))
#define AR_WOW_LEN2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3)
#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i))
#define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */
#define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2015 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 REG_WOW_H
#define REG_WOW_H
#define AR_WOW_PATTERN 0x825C
#define AR_WOW_COUNT 0x8260
#define AR_WOW_BCN_EN 0x8270
#define AR_WOW_BCN_TIMO 0x8274
#define AR_WOW_KEEP_ALIVE_TIMO 0x8278
#define AR_WOW_KEEP_ALIVE 0x827c
#define AR_WOW_KEEP_ALIVE_DELAY 0x8288
#define AR_WOW_PATTERN_MATCH 0x828c
/*
* AR_WOW_LENGTH1
* bit 31:24 pattern 0 length
* bit 23:16 pattern 1 length
* bit 15:8 pattern 2 length
* bit 7:0 pattern 3 length
*
* AR_WOW_LENGTH2
* bit 31:24 pattern 4 length
* bit 23:16 pattern 5 length
* bit 15:8 pattern 6 length
* bit 7:0 pattern 7 length
*
* AR_WOW_LENGTH3
* bit 31:24 pattern 8 length
* bit 23:16 pattern 9 length
* bit 15:8 pattern 10 length
* bit 7:0 pattern 11 length
*
* AR_WOW_LENGTH4
* bit 31:24 pattern 12 length
* bit 23:16 pattern 13 length
* bit 15:8 pattern 14 length
* bit 7:0 pattern 15 length
*/
#define AR_WOW_LENGTH1 0x8360
#define AR_WOW_LENGTH2 0X8364
#define AR_WOW_LENGTH3 0X8380
#define AR_WOW_LENGTH4 0X8384
#define AR_WOW_PATTERN_MATCH_LT_256B 0x8368
#define AR_MAC_PCU_WOW4 0x8370
#define AR_SW_WOW_CONTROL 0x20018
#define AR_SW_WOW_ENABLE 0x1
#define AR_SWITCH_TO_REFCLK 0x2
#define AR_RESET_CONTROL 0x4
#define AR_RESET_VALUE_MASK 0x8
#define AR_HW_WOW_DISABLE 0x10
#define AR_CLR_MAC_INTERRUPT 0x20
#define AR_CLR_KA_INTERRUPT 0x40
#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 27) /* in usecs */
#define AR_WOW_MAC_INTR_EN 0x00040000
#define AR_WOW_MAGIC_EN 0x00010000
#define AR_WOW_PATTERN_EN(x) (x & 0xff)
#define AR_WOW_PAT_FOUND_SHIFT 8
#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PAT_FOUND_SHIFT))
#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PAT_FOUND_SHIFT)
#define AR_WOW_MAGIC_PAT_FOUND 0x00020000
#define AR_WOW_MAC_INTR 0x00080000
#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000
#define AR_WOW_BEACON_FAIL 0x00200000
#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \
AR_WOW_MAGIC_PAT_FOUND | \
AR_WOW_KEEP_ALIVE_FAIL | \
AR_WOW_BEACON_FAIL))
#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \
AR_WOW_MAGIC_EN | \
AR_WOW_MAC_INTR_EN | \
AR_WOW_BEACON_FAIL | \
AR_WOW_KEEP_ALIVE_FAIL))
#define AR_WOW_AIFS_CNT(x) (x & 0xff)
#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8)
#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16)
#define AR_WOW_BEACON_FAIL_EN 0x00000001
#define AR_WOW_BEACON_TIMO 0x40000000
#define AR_WOW_KEEP_ALIVE_NEVER 0xffffffff
#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001
#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002
#define AR_WOW_KEEP_ALIVE_DELAY_VALUE 0x000003e8 /* 1 msec */
#define AR_WOW_BMISSTHRESHOLD 0x20
#define AR_WOW_PAT_END_OF_PKT(x) (x & 0xf)
#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8)
#define AR_WOW_PAT_BACKOFF 0x00000004
#define AR_WOW_CNT_AIFS_CNT 0x00000022
#define AR_WOW_CNT_SLOT_CNT 0x00000009
#define AR_WOW_CNT_KA_CNT 0x00000008
#define AR_WOW_TRANSMIT_BUFFER 0xe000
#define AR_WOW_TXBUF(i) (AR_WOW_TRANSMIT_BUFFER + ((i) << 2))
#define AR_WOW_KA_DESC_WORD2 0xe000
#define AR_WOW_TB_PATTERN(i) (0xe100 + (i << 8))
#define AR_WOW_TB_MASK(i) (0xec00 + (i << 5))
#define AR_WOW_PATTERN_SUPPORTED_LEGACY 0xff
#define AR_WOW_PATTERN_SUPPORTED 0xffff
#define AR_WOW_LENGTH_MAX 0xff
#define AR_WOW_LEN1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3)
#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i))
#define AR_WOW_LEN2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3)
#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i))
#define AR_WOW_LEN3_SHIFT(_i) ((0xb - ((_i) & 0xb)) << 0x3)
#define AR_WOW_LENGTH3_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN3_SHIFT(_i))
#define AR_WOW_LEN4_SHIFT(_i) ((0xf - ((_i) & 0xf)) << 0x3)
#define AR_WOW_LENGTH4_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN4_SHIFT(_i))
#endif /* REG_WOW_H */

View file

@ -16,36 +16,43 @@
#include "ath9k.h"
static const struct wiphy_wowlan_support ath9k_wowlan_support = {
static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
.n_patterns = MAX_NUM_USER_PATTERN,
.pattern_min_len = 1,
.pattern_max_len = MAX_PATTERN_SIZE,
};
static void ath9k_wow_map_triggers(struct ath_softc *sc,
struct cfg80211_wowlan *wowlan,
u32 *wow_triggers)
static const struct wiphy_wowlan_support ath9k_wowlan_support = {
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
.n_patterns = MAX_NUM_PATTERN - 2,
.pattern_min_len = 1,
.pattern_max_len = MAX_PATTERN_SIZE,
};
static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
struct cfg80211_wowlan *wowlan)
{
u8 wow_triggers = 0;
if (wowlan->disconnect)
*wow_triggers |= AH_WOW_LINK_CHANGE |
AH_WOW_BEACON_MISS;
wow_triggers |= AH_WOW_LINK_CHANGE |
AH_WOW_BEACON_MISS;
if (wowlan->magic_pkt)
*wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
if (wowlan->n_patterns)
*wow_triggers |= AH_WOW_USER_PATTERN_EN;
sc->wow_enabled = *wow_triggers;
wow_triggers |= AH_WOW_USER_PATTERN_EN;
return wow_triggers;
}
static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
int pattern_count = 0;
int i, byte_cnt;
int ret, i, byte_cnt = 0;
u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
u8 dis_deauth_mask[MAX_PATTERN_SIZE];
@ -80,12 +87,7 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
* | x:x:x:x:x:x -- 22 bytes
*/
/* Create Disassociate Pattern first */
byte_cnt = 0;
/* Fill out the mask with all FF's */
for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
dis_deauth_mask[i] = 0xff;
@ -108,19 +110,17 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
byte_cnt += 6;
/* copy the bssid, its same as the source mac address */
memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
/* Create Disassociate pattern mask */
dis_deauth_mask[0] = 0xfe;
dis_deauth_mask[1] = 0x03;
dis_deauth_mask[2] = 0xc0;
ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
pattern_count, byte_cnt);
ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
pattern_count, byte_cnt);
if (ret)
goto exit;
pattern_count++;
/*
@ -129,59 +129,39 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
*/
dis_deauth_pattern[0] = 0xC0;
ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
pattern_count, byte_cnt);
ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
pattern_count, byte_cnt);
exit:
return ret;
}
static void ath9k_wow_add_pattern(struct ath_softc *sc,
struct cfg80211_wowlan *wowlan)
static int ath9k_wow_add_pattern(struct ath_softc *sc,
struct cfg80211_wowlan *wowlan)
{
struct ath_hw *ah = sc->sc_ah;
struct ath9k_wow_pattern *wow_pattern = NULL;
struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
int mask_len;
u8 wow_pattern[MAX_PATTERN_SIZE];
u8 wow_mask[MAX_PATTERN_SIZE];
int mask_len, ret = 0;
s8 i = 0;
if (!wowlan->n_patterns)
return;
/*
* Add the new user configured patterns
*/
for (i = 0; i < wowlan->n_patterns; i++) {
mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
memset(wow_pattern, 0, MAX_PATTERN_SIZE);
memset(wow_mask, 0, MAX_PATTERN_SIZE);
memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
memcpy(wow_mask, patterns[i].mask, mask_len);
wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
if (!wow_pattern)
return;
/*
* TODO: convert the generic user space pattern to
* appropriate chip specific/802.11 pattern.
*/
mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
patterns[i].pattern_len);
memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
wow_pattern->pattern_len = patterns[i].pattern_len;
/*
* just need to take care of deauth and disssoc pattern,
* make sure we don't overwrite them.
*/
ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
wow_pattern->mask_bytes,
i + 2,
wow_pattern->pattern_len);
kfree(wow_pattern);
ret = ath9k_hw_wow_apply_pattern(ah,
wow_pattern,
wow_mask,
i + 2,
patterns[i].pattern_len);
if (ret)
break;
}
return ret;
}
int ath9k_suspend(struct ieee80211_hw *hw,
@ -190,58 +170,55 @@ int ath9k_suspend(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
u32 wow_triggers_enabled = 0;
u8 triggers;
int ret = 0;
ath9k_deinit_channel_context(sc);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
ath_stop_ani(sc);
if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
ret = -EINVAL;
ath_err(common, "Device not present\n");
ret = -ENODEV;
goto fail_wow;
}
if (WARN_ON(!wowlan)) {
ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
ath_err(common, "None of the WoW triggers enabled\n");
ret = -EINVAL;
goto fail_wow;
}
if (!device_can_wakeup(sc->dev)) {
ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
ret = 1;
goto fail_wow;
}
/*
* none of the sta vifs are associated
* and we are not currently handling multivif
* cases, for instance we have to seperately
* configure 'keep alive frame' for each
* STA.
*/
if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
ath_dbg(common, WOW, "None of the STA vifs are associated\n");
ret = 1;
goto fail_wow;
}
if (sc->cur_chan->nvifs > 1) {
ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
ret = 1;
goto fail_wow;
}
ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
if (ath9k_is_chanctx_enabled()) {
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
ath_dbg(common, WOW,
"Multi-channel WOW is not supported\n");
ret = 1;
goto fail_wow;
}
}
ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
wow_triggers_enabled);
if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
ath_dbg(common, WOW, "None of the STA vifs are associated\n");
ret = 1;
goto fail_wow;
}
triggers = ath9k_wow_map_triggers(sc, wowlan);
if (!triggers) {
ath_dbg(common, WOW, "No valid WoW triggers\n");
ret = 1;
goto fail_wow;
}
ath_cancel_work(sc);
ath_stop_ani(sc);
ath9k_ps_wakeup(sc);
@ -251,10 +228,21 @@ int ath9k_suspend(struct ieee80211_hw *hw,
* Enable wake up on recieving disassoc/deauth
* frame by default.
*/
ath9k_wow_add_disassoc_deauth_pattern(sc);
ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
if (ret) {
ath_err(common,
"Unable to add disassoc/deauth pattern: %d\n", ret);
goto fail_wow;
}
if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
ath9k_wow_add_pattern(sc, wowlan);
if (triggers & AH_WOW_USER_PATTERN_EN) {
ret = ath9k_wow_add_pattern(sc, wowlan);
if (ret) {
ath_err(common,
"Unable to add user pattern: %d\n", ret);
goto fail_wow;
}
}
spin_lock_bh(&sc->sc_pcu_lock);
/*
@ -278,12 +266,12 @@ int ath9k_suspend(struct ieee80211_hw *hw,
synchronize_irq(sc->irq);
tasklet_kill(&sc->intr_tq);
ath9k_hw_wow_enable(ah, wow_triggers_enabled);
ath9k_hw_wow_enable(ah, triggers);
ath9k_ps_restore(sc);
ath_dbg(common, ANY, "WoW enabled in ath9k\n");
atomic_inc(&sc->wow_sleep_proc_intr);
ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
fail_wow:
mutex_unlock(&sc->mutex);
return ret;
@ -294,7 +282,7 @@ int ath9k_resume(struct ieee80211_hw *hw)
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
u32 wow_status;
u8 status;
mutex_lock(&sc->mutex);
@ -309,29 +297,14 @@ int ath9k_resume(struct ieee80211_hw *hw)
spin_unlock_bh(&sc->sc_pcu_lock);
wow_status = ath9k_hw_wow_wakeup(ah);
if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
/*
* some devices may not pick beacon miss
* as the reason they woke up so we add
* that here for that shortcoming.
*/
wow_status |= AH_WOW_BEACON_MISS;
atomic_dec(&sc->wow_got_bmiss_intr);
ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
}
atomic_dec(&sc->wow_sleep_proc_intr);
if (wow_status) {
ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
ath9k_hw_wow_event_to_string(wow_status), wow_status);
}
status = ath9k_hw_wow_wakeup(ah);
ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
ath_restart_work(sc);
ath9k_start_btcoex(sc);
clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
ath9k_ps_restore(sc);
mutex_unlock(&sc->mutex);
@ -341,22 +314,35 @@ int ath9k_resume(struct ieee80211_hw *hw)
void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
mutex_lock(&sc->mutex);
device_init_wakeup(sc->dev, 1);
device_set_wakeup_enable(sc->dev, enabled);
mutex_unlock(&sc->mutex);
ath_dbg(common, WOW, "WoW wakeup source is %s\n",
(enabled) ? "enabled" : "disabled");
}
void ath9k_init_wow(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
(sc->driver_data & ATH9K_PCI_WOW) &&
device_can_wakeup(sc->dev))
hw->wiphy->wowlan = &ath9k_wowlan_support;
if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
hw->wiphy->wowlan = &ath9k_wowlan_support;
else
hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
atomic_set(&sc->wow_sleep_proc_intr, -1);
atomic_set(&sc->wow_got_bmiss_intr, -1);
device_init_wakeup(sc->dev, 1);
}
}
void ath9k_deinit_wow(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
device_init_wakeup(sc->dev, 0);
}

View file

@ -39,12 +39,3 @@ 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

View file

@ -14,7 +14,6 @@ wil6210-y += ioctl.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
wil6210-y += ethtool.o
# for tracing framework to find trace.h

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2012-2015 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
@ -334,6 +334,30 @@ out:
return rc;
}
static void wil_print_crypto(struct wil6210_priv *wil,
struct cfg80211_crypto_settings *c)
{
int i, n;
wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
c->wpa_versions, c->cipher_group);
wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
for (i = 0; i < n; i++)
wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
c->ciphers_pairwise[i]);
wil_dbg_misc(wil, "}\n");
wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
for (i = 0; i < n; i++)
wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
c->akm_suites[i]);
wil_dbg_misc(wil, "}\n");
wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
c->control_port, be16_to_cpu(c->control_port_ethertype),
c->control_port_no_encrypt);
}
static void wil_print_connect_params(struct wil6210_priv *wil,
struct cfg80211_connect_params *sme)
{
@ -348,6 +372,7 @@ static void wil_print_connect_params(struct wil6210_priv *wil,
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");
wil_print_crypto(wil, &sme->crypto);
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
@ -454,6 +479,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
netif_carrier_on(ndev);
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
@ -618,18 +644,6 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
b->assocresp_ies, b->assocresp_ies_len);
}
static void wil_print_crypto(struct wil6210_priv *wil,
struct cfg80211_crypto_settings *c)
{
wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
c->wpa_versions, c->cipher_group);
wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
c->control_port, be16_to_cpu(c->control_port_ethertype),
c->control_port_no_encrypt);
}
static int wil_fix_bcon(struct wil6210_priv *wil,
struct cfg80211_beacon_data *bcon)
{
@ -757,12 +771,12 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
wil->secure_pcp = info->privacy;
netif_carrier_on(ndev);
rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
channel->hw_value);
if (rc)
goto out;
netif_carrier_on(ndev);
netif_carrier_off(ndev);
out:
mutex_unlock(&wil->mutex);
@ -772,23 +786,26 @@ out:
static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *ndev)
{
int rc, rc1;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s()\n", __func__);
netif_carrier_off(ndev);
wil_set_recovery_state(wil, fw_recovery_idle);
mutex_lock(&wil->mutex);
rc = wmi_pcp_stop(wil);
wmi_pcp_stop(wil);
__wil_down(wil);
rc1 = __wil_up(wil);
__wil_up(wil);
mutex_unlock(&wil->mutex);
return min(rc, rc1);
/* some functions above might fail (e.g. __wil_up). Nevertheless, we
* return success because AP has stopped
*/
return 0;
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
@ -804,6 +821,96 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
return 0;
}
/* probe_client handling */
static void wil_probe_client_handle(struct wil6210_priv *wil,
struct wil_probe_client_req *req)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wil_sta_info *sta = &wil->sta[req->cid];
/* assume STA is alive if it is still connected,
* else FW will disconnect it
*/
bool alive = (sta->status == wil_sta_connected);
cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL);
}
static struct list_head *next_probe_client(struct wil6210_priv *wil)
{
struct list_head *ret = NULL;
mutex_lock(&wil->probe_client_mutex);
if (!list_empty(&wil->probe_client_pending)) {
ret = wil->probe_client_pending.next;
list_del(ret);
}
mutex_unlock(&wil->probe_client_mutex);
return ret;
}
void wil_probe_client_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
probe_client_worker);
struct wil_probe_client_req *req;
struct list_head *lh;
while ((lh = next_probe_client(wil)) != NULL) {
req = list_entry(lh, struct wil_probe_client_req, list);
wil_probe_client_handle(wil, req);
kfree(req);
}
}
void wil_probe_client_flush(struct wil6210_priv *wil)
{
struct wil_probe_client_req *req, *t;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->probe_client_mutex);
list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) {
list_del(&req->list);
kfree(req);
}
mutex_unlock(&wil->probe_client_mutex);
}
static int wil_cfg80211_probe_client(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u64 *cookie)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wil_probe_client_req *req;
int cid = wil_find_cid(wil, peer);
wil_dbg_misc(wil, "%s(%pM => CID %d)\n", __func__, peer, cid);
if (cid < 0)
return -ENOLINK;
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->cid = cid;
req->cookie = cid;
mutex_lock(&wil->probe_client_mutex);
list_add_tail(&req->list, &wil->probe_client_pending);
mutex_unlock(&wil->probe_client_mutex);
*cookie = req->cookie;
queue_work(wil->wq_service, &wil->probe_client_worker);
return 0;
}
static struct cfg80211_ops wil_cfg80211_ops = {
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
@ -823,6 +930,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
.del_station = wil_cfg80211_del_station,
.probe_client = wil_cfg80211_probe_client,
};
static void wil_wiphy_init(struct wiphy *wiphy)
@ -854,6 +962,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
wiphy->cipher_suites = wil_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
wiphy->mgmt_stypes = wil_mgmt_stypes;
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
}
struct wireless_dev *wil_cfg80211_init(struct device *dev)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2012-2015 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
@ -33,34 +33,6 @@ 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.");
static unsigned int tx_interframe_timeout =
WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT;
module_param(tx_interframe_timeout, uint, S_IRUGO);
MODULE_PARM_DESC(tx_interframe_timeout,
" Interrupt moderation TX interframe timeout, usecs.");
static unsigned int rx_interframe_timeout =
WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT;
module_param(rx_interframe_timeout, uint, S_IRUGO);
MODULE_PARM_DESC(rx_interframe_timeout,
" Interrupt moderation RX interframe timeout, usecs.");
static unsigned int tx_max_burst_duration =
WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT;
module_param(tx_max_burst_duration, uint, S_IRUGO);
MODULE_PARM_DESC(tx_max_burst_duration,
" Interrupt moderation TX max burst duration, usecs.");
static unsigned int rx_max_burst_duration =
WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT;
module_param(rx_max_burst_duration, uint, S_IRUGO);
MODULE_PARM_DESC(rx_max_burst_duration,
" Interrupt moderation RX max burst duration, usecs.");
/* if not set via modparam, will be set to default value of 1/8 of
* rx ring size during init flow
*/
@ -248,7 +220,9 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
wil_link_off(wil);
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
if (test_bit(wil_status_fwconnected, wil->status)) {
clear_bit(wil_status_fwconnected, wil->status);
cfg80211_disconnected(ndev, reason_code,
@ -395,6 +369,8 @@ static void wil_connect_worker(struct work_struct *work)
int rc;
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
connect_worker);
struct net_device *ndev = wil_to_ndev(wil);
int cid = wil->pending_connect_cid;
int ringid = wil_find_free_vring(wil);
@ -409,7 +385,7 @@ static void wil_connect_worker(struct work_struct *work)
wil->pending_connect_cid = -1;
if (rc == 0) {
wil->sta[cid].status = wil_sta_connected;
wil_link_on(wil);
netif_tx_wake_all_queues(ndev);
} else {
wil->sta[cid].status = wil_sta_unused;
}
@ -429,6 +405,7 @@ int wil_priv_init(struct wil6210_priv *wil)
mutex_init(&wil->wmi_mutex);
mutex_init(&wil->back_rx_mutex);
mutex_init(&wil->back_tx_mutex);
mutex_init(&wil->probe_client_mutex);
init_completion(&wil->wmi_ready);
init_completion(&wil->wmi_call);
@ -443,10 +420,12 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker);
INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker);
INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
INIT_LIST_HEAD(&wil->back_rx_pending);
INIT_LIST_HEAD(&wil->back_tx_pending);
INIT_LIST_HEAD(&wil->probe_client_pending);
spin_lock_init(&wil->wmi_ev_lock);
init_waitqueue_head(&wil->wq);
@ -459,10 +438,10 @@ int wil_priv_init(struct wil6210_priv *wil)
goto out_wmi_wq;
wil->last_fw_recovery = jiffies;
wil->tx_interframe_timeout = tx_interframe_timeout;
wil->rx_interframe_timeout = rx_interframe_timeout;
wil->tx_max_burst_duration = tx_max_burst_duration;
wil->rx_max_burst_duration = rx_max_burst_duration;
wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT;
wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT;
wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT;
wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT;
if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
@ -509,6 +488,8 @@ void wil_priv_deinit(struct wil6210_priv *wil)
cancel_work_sync(&wil->back_rx_worker);
wil_back_tx_flush(wil);
cancel_work_sync(&wil->back_tx_worker);
wil_probe_client_flush(wil);
cancel_work_sync(&wil->probe_client_worker);
destroy_workqueue(wil->wq_service);
destroy_workqueue(wil->wmi_wq);
}
@ -741,28 +722,6 @@ void wil_fw_error_recovery(struct wil6210_priv *wil)
schedule_work(&wil->fw_error_worker);
}
void wil_link_on(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
netif_carrier_on(ndev);
wil_dbg_misc(wil, "netif_tx_wake : link on\n");
netif_tx_wake_all_queues(ndev);
}
void wil_link_off(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
netif_tx_stop_all_queues(ndev);
wil_dbg_misc(wil, "netif_tx_stop : link off\n");
netif_carrier_off(ndev);
}
int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2012-2015 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
@ -15,7 +15,6 @@
*/
#include <linux/etherdevice.h>
#include "wil6210.h"
#include "txrx.h"
@ -122,6 +121,12 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
return min(tx_done, budget);
}
static void wil_dev_setup(struct net_device *dev)
{
ether_setup(dev);
dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT;
}
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
@ -153,7 +158,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup);
ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup);
if (!ndev) {
dev_err(dev, "alloc_netdev_mqs failed\n");
rc = -ENOMEM;
@ -174,7 +179,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
wil_link_off(wil);
netif_tx_stop_all_queues(ndev);
return wil;
@ -217,8 +222,6 @@ int wil_if_add(struct wil6210_priv *wil)
return rc;
}
wil_link_off(wil);
return 0;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Qualcomm Atheros, Inc.
* Copyright (c) 2014-2015 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
@ -292,6 +292,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
u16 agg_timeout = req->ba_timeout;
u16 status = WLAN_STATUS_SUCCESS;
u16 ssn = req->ba_seq_ctrl >> 4;
struct wil_tid_ampdu_rx *r;
int rc;
might_sleep();
@ -328,11 +329,10 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
return;
/* apply */
r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn);
spin_lock_bh(&sta->tid_rx_lock);
wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]);
sta->tid_rx[tid] = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn);
sta->tid_rx[tid] = r;
spin_unlock_bh(&sta->tid_rx_lock);
}

View file

@ -671,6 +671,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
}
memset(txdata, 0, sizeof(*txdata));
spin_lock_init(&txdata->lock);
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
@ -718,8 +719,10 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
wil_dbg_misc(wil, "%s() id=%d\n", __func__, id);
spin_lock_bh(&txdata->lock);
txdata->enabled = 0; /* no Tx can be in progress or start anew */
spin_unlock_bh(&txdata->lock);
/* make sure NAPI won't touch this vring */
wil->vring_tx_data[id].enabled = 0;
if (test_bit(wil_status_napi_en, wil->status))
napi_synchronize(&wil->napi_tx);
@ -873,9 +876,6 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
d->mac.d[1] = 0;
d->mac.d[2] = 0;
d->mac.ucode_cmd = 0;
/* use dst index 0 */
d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) |
(0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS);
/* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */
d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) |
(1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS);
@ -938,8 +938,8 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
return 0;
}
static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
struct vring_tx_desc dd, *d = &dd;
@ -955,18 +955,21 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_dbg_txrx(wil, "%s()\n", __func__);
if (unlikely(!txdata->enabled))
return -EINVAL;
if (avail < 1 + nr_frags) {
wil_err_ratelimited(wil,
"Tx ring full. No space for %d fragments\n",
1 + nr_frags);
"Tx ring[%2d] full. No space for %d fragments\n",
vring_index, 1 + nr_frags);
return -ENOMEM;
}
_d = &vring->va[i].tx;
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);
wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", vring_index,
skb_headlen(skb), skb->data, &pa);
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
@ -977,15 +980,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
/* Process TCP/UDP checksum offloading */
if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n",
vring_index);
goto dma_error;
}
vring->ctx[i].nr_frags = nr_frags;
wil_tx_desc_set_nr_frags(d, nr_frags);
if (nr_frags)
*_d = *d;
/* middle segments */
for (; f < nr_frags; f++) {
@ -993,6 +994,10 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
*_d = *d;
wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
i = (swhead + f + 1) % vring->size;
_d = &vring->va[i].tx;
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
@ -1006,13 +1011,15 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
* it will succeed here too
*/
wil_tx_desc_offload_cksum_set(wil, d, skb);
*_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
*_d = *d;
wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
/* hold reference to skb
* to prevent skb release before accounting
@ -1020,15 +1027,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
*/
vring->ctx[i].skb = skb_get(skb);
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
if (wil_vring_is_empty(vring)) /* performance monitoring */
txdata->idle += get_cycles() - txdata->last_idle;
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", vring_index, swhead,
vring->swhead);
trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
@ -1055,6 +1060,19 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
return -EINVAL;
}
static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
int vring_index = vring - wil->vring_tx;
struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
int rc;
spin_lock(&txdata->lock);
rc = __wil_tx_vring(wil, vring, skb);
spin_unlock(&txdata->lock);
return rc;
}
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
@ -1121,6 +1139,22 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
return NET_XMIT_DROP;
}
static inline bool wil_need_txstat(struct sk_buff *skb)
{
struct ethhdr *eth = (void *)skb->data;
return is_unicast_ether_addr(eth->h_dest) && skb->sk &&
(skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS);
}
static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
{
if (unlikely(wil_need_txstat(skb)))
skb_complete_wifi_ack(skb, acked);
else
acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb);
}
/**
* Clean up transmitted skb's from the Tx VRING
*
@ -1181,10 +1215,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
d->dma.error);
wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
vring->swtail, dmalen, d->dma.status,
d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
"TxC[%2d][%3d] : %d bytes, status 0x%02x err 0x%02x\n",
ringid, vring->swtail, dmalen,
d->dma.status, d->dma.error);
wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil_txdesc_unmap(dev, d, ctx);
@ -1199,8 +1233,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++;
stats->tx_errors++;
}
dev_kfree_skb_any(skb);
wil_consume_skb(skb, d->dma.error == 0);
}
memset(ctx, 0, sizeof(*ctx));
/* There is no need to touch HW descriptor:

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2012-2015 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
@ -44,8 +44,9 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (9)
#define WIL_TX_RING_SIZE_ORDER_DEFAULT (9)
#define WIL_TX_Q_LEN_DEFAULT (4000)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
#define WIL_TX_RING_SIZE_ORDER_DEFAULT (10)
/* limit ring size in range [32..32k] */
#define WIL_RING_SIZE_ORDER_MIN (5)
#define WIL_RING_SIZE_ORDER_MAX (15)
@ -77,8 +78,8 @@ static inline u32 wil_mtu2macbuf(u32 mtu)
#define WIL_MAX_ETH_MTU (IEEE80211_MAX_DATA_LEN_DMG - 8)
/* Max supported by wil6210 value for interrupt threshold is 5sec. */
#define WIL6210_ITR_TRSH_MAX (5000000)
#define WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT (15) /* usec */
#define WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT (15) /* usec */
#define WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */
#define WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */
#define WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT (500) /* usec */
#define WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT (500) /* usec */
#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */
@ -384,6 +385,7 @@ struct vring_tx_data {
u16 agg_timeout;
u8 agg_amsdu;
bool addba_in_progress; /* if set, agg_xxx is for request in progress */
spinlock_t lock;
};
enum { /* for wil6210_priv.status */
@ -503,6 +505,12 @@ struct wil_back_tx {
u16 agg_timeout;
};
struct wil_probe_client_req {
struct list_head list;
u64 cookie;
u8 cid;
};
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
@ -563,6 +571,10 @@ struct wil6210_priv {
struct list_head back_tx_pending;
struct mutex back_tx_mutex; /* protect @back_tx_pending */
struct work_struct back_tx_worker;
/* keep alive */
struct list_head probe_client_pending;
struct mutex probe_client_mutex; /* protect @probe_client_pending */
struct work_struct probe_client_worker;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@ -649,8 +661,6 @@ void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil);
void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_set_recovery_state(struct wil6210_priv *wil, int state);
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);
@ -723,6 +733,8 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
int wmi_pcp_stop(struct wil6210_priv *wil);
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
u16 reason_code, bool from_event);
void wil_probe_client_flush(struct wil6210_priv *wil);
void wil_probe_client_worker(struct work_struct *work);
int wil_rx_init(struct wil6210_priv *wil, u16 size);
void wil_rx_fini(struct wil6210_priv *wil);

View file

@ -17,10 +17,6 @@
#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
*
@ -37,13 +33,7 @@ void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops)
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 */
/* platform specific init functions should be called here */
return handle;
}

View file

@ -1,257 +0,0 @@
/*
* 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;
}

View file

@ -1,24 +0,0 @@
/*
* 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__ */

View file

@ -566,7 +566,6 @@ static void wil_addba_tx_cid(struct wil6210_priv *wil, u8 cid, u16 wsize)
static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wmi_data_port_open_event *evt = d;
u8 cid = evt->cid;
@ -580,7 +579,6 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
wil->sta[cid].data_port_open = true;
if (agg_wsize >= 0)
wil_addba_tx_cid(wil, cid, agg_wsize);
netif_carrier_on(ndev);
}
static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)
@ -1133,12 +1131,13 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
return rc;
}
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
{
int rc;
struct wmi_temp_sense_cmd cmd = {
.measure_marlon_m_en = cpu_to_le32(!!t_m),
.measure_marlon_r_en = cpu_to_le32(!!t_r),
.measure_baseband_en = cpu_to_le32(!!t_bb),
.measure_rf_en = cpu_to_le32(!!t_rf),
.measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW),
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
@ -1150,10 +1149,10 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
if (rc)
return rc;
if (t_m)
*t_m = le32_to_cpu(reply.evt.marlon_m_t1000);
if (t_r)
*t_r = le32_to_cpu(reply.evt.marlon_r_t1000);
if (t_bb)
*t_bb = le32_to_cpu(reply.evt.baseband_t1000);
if (t_rf)
*t_rf = le32_to_cpu(reply.evt.rf_t1000);
return 0;
}

View file

@ -29,8 +29,10 @@
/* General */
#define WILOCITY_MAX_ASSOC_STA (8)
#define WILOCITY_DEFAULT_ASSOC_STA (1)
#define WMI_MAC_LEN (6)
#define WMI_PROX_RANGE_NUM (3)
#define WMI_MAX_LOSS_DMG_BEACONS (32)
/* List of Commands */
enum wmi_command_id {
@ -48,7 +50,7 @@ enum wmi_command_id {
WMI_SET_WSC_STATUS_CMDID = 0x0041,
WMI_PXMT_RANGE_CFG_CMDID = 0x0042,
WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043,
WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300,
/* WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, */
WMI_MEM_READ_CMDID = 0x0800,
WMI_MEM_WR_CMDID = 0x0801,
WMI_ECHO_CMDID = 0x0803,
@ -102,6 +104,8 @@ enum wmi_command_id {
WMI_MAINTAIN_RESUME_CMDID = 0x0851,
WMI_RS_MGMT_CMDID = 0x0852,
WMI_RF_MGMT_CMDID = 0x0853,
WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x0854,
WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x0855,
/* Performance monitoring commands */
WMI_BF_CTRL_CMDID = 0x0862,
WMI_NOTIFY_REQ_CMDID = 0x0863,
@ -136,6 +140,7 @@ enum wmi_command_id {
WMI_EAPOL_TX_CMDID = 0xf04c,
WMI_MAC_ADDR_REQ_CMDID = 0xf04d,
WMI_FW_VER_CMDID = 0xf04e,
WMI_PMC_CMDID = 0xf04f,
};
/*
@ -283,8 +288,8 @@ enum wmi_scan_type {
WMI_LONG_SCAN = 0,
WMI_SHORT_SCAN = 1,
WMI_PBC_SCAN = 2,
WMI_ACTIVE_SCAN = 3,
WMI_DIRECT_SCAN = 4,
WMI_DIRECT_SCAN = 3,
WMI_ACTIVE_SCAN = 4,
};
struct wmi_start_scan_cmd {
@ -374,6 +379,17 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
/*
* WMI_THERMAL_THROTTLING_CTRL_CMDID
*/
#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF)
struct wmi_thermal_throttling_ctrl_cmd {
__le32 time_on_usec;
__le32 time_off_usec;
__le32 max_txop_length_usec;
} __packed;
/*
* WMI_RF_RX_TEST_CMDID
*/
@ -648,6 +664,7 @@ enum wmi_cfg_rx_chain_cmd_action {
enum wmi_cfg_rx_chain_cmd_decap_trans_type {
WMI_DECAP_TYPE_802_3 = 0,
WMI_DECAP_TYPE_NATIVE_WIFI = 1,
WMI_DECAP_TYPE_NONE = 2,
};
enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
@ -785,9 +802,17 @@ struct wmi_echo_cmd {
*
* Measure MAC and radio temperatures
*/
/* Possible modes for temperature measurement */
enum wmi_temperature_measure_mode {
TEMPERATURE_USE_OLD_VALUE = 0x1,
TEMPERATURE_MEASURE_NOW = 0x2,
};
struct wmi_temp_sense_cmd {
__le32 measure_marlon_m_en;
__le32 measure_marlon_r_en;
__le32 measure_baseband_en;
__le32 measure_rf_en;
__le32 measure_mode;
} __packed;
/*
@ -843,6 +868,7 @@ enum wmi_event_id {
WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
WMI_RS_MGMT_DONE_EVENTID = 0x1852,
WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
@ -859,6 +885,7 @@ enum wmi_event_id {
WMI_FLASH_READ_DONE_EVENTID = 0x1902,
WMI_FLASH_WRITE_DONE_EVENTID = 0x1903,
/*P2P*/
WMI_P2P_CFG_DONE_EVENTID = 0x1910,
WMI_PORT_ALLOCATED_EVENTID = 0x1911,
WMI_PORT_DELETED_EVENTID = 0x1912,
WMI_LISTEN_STARTED_EVENTID = 0x1914,
@ -898,6 +925,15 @@ struct wmi_rf_mgmt_status_event {
__le32 rf_status;
} __packed;
/*
* WMI_THERMAL_THROTTLING_STATUS_EVENTID
*/
struct wmi_thermal_throttling_status_event {
__le32 time_on_usec;
__le32 time_off_usec;
__le32 max_txop_length_usec;
} __packed;
/*
* WMI_GET_STATUS_DONE_EVENTID
*/
@ -1154,6 +1190,14 @@ struct wmi_get_pcp_channel_event {
u8 reserved[3];
} __packed;
/*
* WMI_P2P_CFG_DONE_EVENTID
*/
struct wmi_p2p_cfg_done_event {
u8 status; /* wmi_fw_status */
u8 reserved[3];
} __packed;
/*
* WMI_PORT_ALLOCATED_EVENTID
*/
@ -1282,8 +1326,8 @@ struct wmi_echo_event {
* Measure MAC and radio temperatures
*/
struct wmi_temp_sense_done_event {
__le32 marlon_m_t1000;
__le32 marlon_r_t1000;
__le32 baseband_t1000;
__le32 rf_t1000;
} __packed;
#endif /* __WILOCITY_WMI_H__ */

View file

@ -166,6 +166,15 @@ config B43_PHY_LCN
Say N, this is BROKEN and crashes driver.
config B43_PHY_AC
bool "Support for AC-PHY (802.11ac) devices (BROKEN)"
depends on B43 && B43_BCMA && BROKEN
---help---
This PHY type can be found in the following chipsets:
PCI: BCM4352, BCM4360
Say N, this is BROKEN and crashes driver.
# This config option automatically enables b43 LEDS support,
# if it's possible.
config B43_LEDS

View file

@ -13,6 +13,7 @@ b43-$(CONFIG_B43_PHY_HT) += phy_ht.o
b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o
b43-$(CONFIG_B43_PHY_HT) += radio_2059.o
b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o
b43-$(CONFIG_B43_PHY_AC) += phy_ac.o
b43-y += sysfs.o
b43-y += xmit.o
b43-y += dma.o

View file

@ -500,6 +500,8 @@ enum {
#define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */
#define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */
#define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */
#define B43_BCMA_IOCTL_PHY_BW_80MHZ 0x000000C0 /* 80 MHz bandwidth */
#define B43_BCMA_IOCTL_DAC 0x00000300 /* Highspeed DAC mode control field */
#define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */
/* BCMA 802.11 core specific IO status (BCMA_IOST) flags */
@ -941,6 +943,7 @@ struct b43_wl {
bool beacon1_uploaded;
bool beacon_templates_virgin; /* Never wrote the templates? */
struct work_struct beacon_update_trigger;
spinlock_t beacon_lock;
/* The current QOS parameters for the 4 queues. */
struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM];

View file

@ -1262,6 +1262,23 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
flags |= B43_BCMA_IOCTL_GMODE;
b43_device_enable(dev, flags);
if (dev->phy.type == B43_PHYTYPE_AC) {
u16 tmp;
tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
tmp &= ~B43_BCMA_IOCTL_DAC;
tmp |= 0x100;
bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN;
bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
tmp |= B43_BCMA_IOCTL_PHY_CLKEN;
bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
}
bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST);
b43_bcma_phy_reset(dev);
bcma_core_pll_ctl(dev->dev->bdev, req, status, true);
@ -1601,12 +1618,26 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
unsigned int rate;
u16 ctl;
int antenna;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
struct ieee80211_tx_info *info;
unsigned long flags;
struct sk_buff *beacon_skb;
bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
len = min_t(size_t, dev->wl->current_beacon->len,
0x200 - sizeof(struct b43_plcp_hdr6));
spin_lock_irqsave(&dev->wl->beacon_lock, flags);
info = IEEE80211_SKB_CB(dev->wl->current_beacon);
rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
/* Clone the beacon, so it cannot go away, while we write it to hw. */
beacon_skb = skb_clone(dev->wl->current_beacon, GFP_ATOMIC);
spin_unlock_irqrestore(&dev->wl->beacon_lock, flags);
if (!beacon_skb) {
b43dbg(dev->wl, "Could not upload beacon. "
"Failed to clone beacon skb.");
return;
}
bcn = (const struct ieee80211_mgmt *)(beacon_skb->data);
len = min_t(size_t, beacon_skb->len,
0x200 - sizeof(struct b43_plcp_hdr6));
b43_write_template_common(dev, (const u8 *)bcn,
len, ram_offset, shm_size_offset, rate);
@ -1674,6 +1705,8 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
B43_SHM_SH_DTIMPER, 0);
}
b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset);
dev_kfree_skb_any(beacon_skb);
}
static void b43_upload_beacon0(struct b43_wldev *dev)
@ -1790,13 +1823,13 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
mutex_unlock(&wl->mutex);
}
/* Asynchronously update the packet templates in template RAM.
* Locking: Requires wl->mutex to be locked. */
/* Asynchronously update the packet templates in template RAM. */
static void b43_update_templates(struct b43_wl *wl)
{
struct sk_buff *beacon;
struct sk_buff *beacon, *old_beacon;
unsigned long flags;
/* This is the top half of the ansynchronous beacon update.
/* This is the top half of the asynchronous beacon update.
* The bottom half is the beacon IRQ.
* Beacon update must be asynchronous to avoid sending an
* invalid beacon. This can happen for example, if the firmware
@ -1810,12 +1843,17 @@ static void b43_update_templates(struct b43_wl *wl)
if (unlikely(!beacon))
return;
if (wl->current_beacon)
dev_kfree_skb_any(wl->current_beacon);
spin_lock_irqsave(&wl->beacon_lock, flags);
old_beacon = wl->current_beacon;
wl->current_beacon = beacon;
wl->beacon0_uploaded = false;
wl->beacon1_uploaded = false;
spin_unlock_irqrestore(&wl->beacon_lock, flags);
ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger);
if (old_beacon)
dev_kfree_skb_any(old_beacon);
}
static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
@ -4505,6 +4543,12 @@ static int b43_phy_versioning(struct b43_wldev *dev)
if (phy_rev > 1)
unsupported = 1;
break;
#endif
#ifdef CONFIG_B43_PHY_AC
case B43_PHYTYPE_AC:
if (phy_rev > 1)
unsupported = 1;
break;
#endif
default:
unsupported = 1;
@ -4602,6 +4646,10 @@ static int b43_phy_versioning(struct b43_wldev *dev)
if (radio_id != 0x2064)
unsupported = 1;
break;
case B43_PHYTYPE_AC:
if (radio_id != 0x2069)
unsupported = 1;
break;
default:
B43_WARN_ON(1);
}
@ -5095,7 +5143,6 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw,
{
struct b43_wl *wl = hw_to_b43_wl(hw);
/* FIXME: add locking */
b43_update_templates(wl);
return 0;
@ -5585,6 +5632,7 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
wl->hw = hw;
mutex_init(&wl->mutex);
spin_lock_init(&wl->hardirq_lock);
spin_lock_init(&wl->beacon_lock);
INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
INIT_WORK(&wl->tx_work, b43_tx_work);

View file

@ -0,0 +1,92 @@
/*
* Broadcom B43 wireless driver
* IEEE 802.11ac AC-PHY support
*
* Copyright (c) 2015 Rafał Miłecki <zajec5@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include "b43.h"
#include "phy_ac.h"
/**************************************************
* Basic PHY ops
**************************************************/
static int b43_phy_ac_op_allocate(struct b43_wldev *dev)
{
struct b43_phy_ac *phy_ac;
phy_ac = kzalloc(sizeof(*phy_ac), GFP_KERNEL);
if (!phy_ac)
return -ENOMEM;
dev->phy.ac = phy_ac;
return 0;
}
static void b43_phy_ac_op_free(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_ac *phy_ac = phy->ac;
kfree(phy_ac);
phy->ac = NULL;
}
static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
u16 set)
{
b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg);
b43_write16(dev, B43_MMIO_PHY_DATA,
(b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
}
static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg)
{
b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
return b43_read16(dev, B43_MMIO_RADIO24_DATA);
}
static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg,
u16 value)
{
b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
}
static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev)
{
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
return 11;
return 36;
}
static enum b43_txpwr_result
b43_phy_ac_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
{
return B43_TXPWR_RES_DONE;
}
static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev)
{
}
/**************************************************
* PHY ops struct
**************************************************/
const struct b43_phy_operations b43_phyops_ac = {
.allocate = b43_phy_ac_op_allocate,
.free = b43_phy_ac_op_free,
.phy_maskset = b43_phy_ac_op_maskset,
.radio_read = b43_phy_ac_op_radio_read,
.radio_write = b43_phy_ac_op_radio_write,
.get_default_chan = b43_phy_ac_op_get_default_chan,
.recalc_txpower = b43_phy_ac_op_recalc_txpower,
.adjust_txpower = b43_phy_ac_op_adjust_txpower,
};

View file

@ -0,0 +1,38 @@
#ifndef B43_PHY_AC_H_
#define B43_PHY_AC_H_
#include "phy_common.h"
#define B43_PHY_AC_BBCFG 0x001
#define B43_PHY_AC_BBCFG_RSTCCA 0x4000 /* Reset CCA */
#define B43_PHY_AC_BANDCTL 0x003 /* Band control */
#define B43_PHY_AC_BANDCTL_5GHZ 0x0001
#define B43_PHY_AC_TABLE_ID 0x00d
#define B43_PHY_AC_TABLE_OFFSET 0x00e
#define B43_PHY_AC_TABLE_DATA1 0x00f
#define B43_PHY_AC_TABLE_DATA2 0x010
#define B43_PHY_AC_TABLE_DATA3 0x011
#define B43_PHY_AC_CLASSCTL 0x140 /* Classifier control */
#define B43_PHY_AC_CLASSCTL_CCKEN 0x0001 /* CCK enable */
#define B43_PHY_AC_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */
#define B43_PHY_AC_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */
#define B43_PHY_AC_BW1A 0x371
#define B43_PHY_AC_BW2 0x372
#define B43_PHY_AC_BW3 0x373
#define B43_PHY_AC_BW4 0x374
#define B43_PHY_AC_BW5 0x375
#define B43_PHY_AC_BW6 0x376
#define B43_PHY_AC_RFCTL_CMD 0x408
#define B43_PHY_AC_C1_CLIP 0x6d4
#define B43_PHY_AC_C1_CLIP_DIS 0x4000
#define B43_PHY_AC_C2_CLIP 0x8d4
#define B43_PHY_AC_C2_CLIP_DIS 0x4000
#define B43_PHY_AC_C3_CLIP 0xad4
#define B43_PHY_AC_C3_CLIP_DIS 0x4000
struct b43_phy_ac {
};
extern const struct b43_phy_operations b43_phyops_ac;
#endif /* B43_PHY_AC_H_ */

View file

@ -33,6 +33,7 @@
#include "phy_lp.h"
#include "phy_ht.h"
#include "phy_lcn.h"
#include "phy_ac.h"
#include "b43.h"
#include "main.h"
@ -68,6 +69,11 @@ int b43_phy_allocate(struct b43_wldev *dev)
case B43_PHYTYPE_LCN:
#ifdef CONFIG_B43_PHY_LCN
phy->ops = &b43_phyops_lcn;
#endif
break;
case B43_PHYTYPE_AC:
#ifdef CONFIG_B43_PHY_AC
phy->ops = &b43_phyops_ac;
#endif
break;
}
@ -572,7 +578,8 @@ void b43_phy_force_clock(struct b43_wldev *dev, bool force)
u32 tmp;
WARN_ON(dev->phy.type != B43_PHYTYPE_N &&
dev->phy.type != B43_PHYTYPE_HT);
dev->phy.type != B43_PHYTYPE_HT &&
dev->phy.type != B43_PHYTYPE_AC);
switch (dev->dev->bus_type) {
#ifdef CONFIG_B43_BCMA

View file

@ -222,6 +222,8 @@ struct b43_phy {
struct b43_phy_ht *ht;
/* LCN-PHY specific information */
struct b43_phy_lcn *lcn;
/* AC-PHY specific information */
struct b43_phy_ac *ac;
};
/* Band support flags. */

View file

@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
{
}
static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
{
bool is_err = false;
#ifdef CONFIG_PM_SLEEP
is_err = atomic_read(&sdiodev->suspend);
#endif
return is_err;
}
static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
wait_queue_head_t *wq)
{
#ifdef CONFIG_PM_SLEEP
int retry = 0;
while (atomic_read(&sdiodev->suspend) && retry++ != 30)
wait_event_timeout(*wq, false, HZ/100);
#endif
}
int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
{
int ret = 0;
@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
write, fn, addr, regsz);
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
/* only allow byte access on F0 */
if (WARN_ON(regsz > 1 && !fn))
return -EINVAL;
@ -292,6 +269,12 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
return ret;
}
static void brcmf_sdiod_nomedium_state(struct brcmf_sdio_dev *sdiodev)
{
sdiodev->state = BRCMF_STATE_NOMEDIUM;
brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
}
static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 regsz, void *data, bool write)
{
@ -299,7 +282,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
s32 retry = 0;
int ret;
if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM)
if (sdiodev->state == BRCMF_STATE_NOMEDIUM)
return -ENOMEDIUM;
/*
@ -325,7 +308,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
if (ret == -ENOMEDIUM)
brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM);
brcmf_sdiod_nomedium_state(sdiodev);
else if (ret != 0) {
/*
* SleepCSR register access can fail when
@ -348,7 +331,7 @@ brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
int err = 0, i;
u8 addr[3];
if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM)
if (sdiodev->state == BRCMF_STATE_NOMEDIUM)
return -ENOMEDIUM;
addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
@ -462,10 +445,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
unsigned int req_sz;
int err;
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
/* Single skb use the standard mmc interface */
req_sz = pkt->len + 3;
req_sz &= (uint)~3;
@ -481,7 +460,7 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
req_sz);
if (err == -ENOMEDIUM)
brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM);
brcmf_sdiod_nomedium_state(sdiodev);
return err;
}
@ -516,10 +495,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
if (!pktlist->qlen)
return -EINVAL;
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
if (brcmf_sdiod_pm_resume_error(sdiodev))
return -EIO;
target_list = pktlist;
/* for host with broken sg support, prepare a page aligned list */
__skb_queue_head_init(&local_list);
@ -620,8 +595,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
if (ret == -ENOMEDIUM) {
brcmf_bus_change_state(sdiodev->bus_if,
BRCMF_BUS_NOMEDIUM);
brcmf_sdiod_nomedium_state(sdiodev);
break;
} else if (ret != 0) {
brcmf_err("CMD53 sg block %s failed %d\n",
@ -1076,9 +1050,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
bus_if->wowl_supported = true;
#endif
sdiodev->sleeping = false;
atomic_set(&sdiodev->suspend, false);
init_waitqueue_head(&sdiodev->request_word_wait);
init_waitqueue_head(&sdiodev->request_buffer_wait);
init_waitqueue_head(&sdiodev->idle_wait);
brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
err = brcmf_sdiod_probe(sdiodev);
@ -1140,12 +1114,23 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
#ifdef CONFIG_PM_SLEEP
static int brcmf_ops_sdio_suspend(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_bus *bus_if;
struct brcmf_sdio_dev *sdiodev;
mmc_pm_flag_t sdio_flags;
brcmf_dbg(SDIO, "Enter\n");
bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
/* wait for watchdog to go idle */
if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping,
msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) {
brcmf_err("bus still active\n");
return -EBUSY;
}
/* disable watchdog */
brcmf_sdio_wd_timer(sdiodev->bus, 0);
atomic_set(&sdiodev->suspend, true);
if (sdiodev->wowl_enabled) {
@ -1157,9 +1142,6 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
}
brcmf_sdio_wd_timer(sdiodev->bus, 0);
return 0;
}

View file

@ -33,11 +33,8 @@
/* The level of bus communication with the dongle */
enum brcmf_bus_state {
BRCMF_BUS_UNKNOWN, /* Not determined yet */
BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */
BRCMF_BUS_DOWN, /* Not ready for frame transfers */
BRCMF_BUS_LOAD, /* Download access only (CPU reset) */
BRCMF_BUS_DATA /* Ready for frame transfers */
BRCMF_BUS_UP /* Ready for frame transfers */
};
/* The level of bus communication with the dongle */
@ -188,22 +185,6 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
bus->ops->wowl_config(bus->dev, enabled);
}
static inline bool brcmf_bus_ready(struct brcmf_bus *bus)
{
return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA;
}
static inline void brcmf_bus_change_state(struct brcmf_bus *bus,
enum brcmf_bus_state new_state)
{
/* NOMEDIUM is permanent */
if (bus->state == BRCMF_BUS_NOMEDIUM)
return;
brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state);
bus->state = new_state;
}
/*
* interface functions from common layer
*/
@ -226,6 +207,9 @@ void brcmf_txflowblock(struct device *dev, bool state);
/* Notify the bus has transferred the tx packet to firmware */
void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
/* Configure the "global" bus state used by upper layers */
void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
int brcmf_bus_start(struct device *dev);
s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
void brcmf_bus_add_txhdrlen(struct device *dev, uint len);

View file

@ -1229,7 +1229,25 @@ static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
memset(prof, 0, sizeof(*prof));
}
static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
{
u16 reason;
switch (e->event_code) {
case BRCMF_E_DEAUTH:
case BRCMF_E_DEAUTH_IND:
case BRCMF_E_DISASSOC_IND:
reason = e->reason;
break;
case BRCMF_E_LINK:
default:
reason = 0;
break;
}
return reason;
}
static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
s32 err = 0;
@ -1244,7 +1262,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
brcmf_err("WLC_DISASSOC failed (%d)\n", err);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
GFP_KERNEL);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
@ -1414,7 +1433,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
if (!check_vif_up(ifp->vif))
return -EIO;
brcmf_link_down(ifp->vif);
brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
brcmf_dbg(TRACE, "Exit\n");
@ -3041,7 +3060,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
* disassociate from AP to save power while system is
* in suspended state
*/
brcmf_link_down(vif);
brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
/* Make sure WPA_Supplicant receives all the event
* generated due to DISASSOC call to the fw to keep
* the state fw and WPA_Supplicant state consistent
@ -4927,7 +4946,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
if (!brcmf_is_ibssmode(ifp->vif)) {
brcmf_bss_connect_done(cfg, ndev, e, false);
}
brcmf_link_down(ifp->vif);
brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
brcmf_init_prof(ndev_to_prof(ndev));
if (ndev != cfg_to_ndev(cfg))
complete(&cfg->vif_disabled);
@ -5868,7 +5887,7 @@ static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
* from AP to save power
*/
if (check_vif_up(ifp->vif)) {
brcmf_link_down(ifp->vif);
brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
/* Make sure WPA_Supplicant receives all the event
generated due to DISASSOC call to the fw to keep

View file

@ -41,6 +41,8 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
u8 buf[BRCMF_DCMD_SMLEN];
struct brcmf_join_pref_params join_pref_params[2];
struct brcmf_rev_info_le revinfo;
struct brcmf_rev_info *ri;
char *ptr;
s32 err;
@ -48,12 +50,37 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
sizeof(ifp->mac_addr));
if (err < 0) {
brcmf_err("Retreiving cur_etheraddr failed, %d\n",
err);
brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
goto done;
}
memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
&revinfo, sizeof(revinfo));
ri = &ifp->drvr->revinfo;
if (err < 0) {
brcmf_err("retrieving revision info failed, %d\n", err);
} else {
ri->vendorid = le32_to_cpu(revinfo.vendorid);
ri->deviceid = le32_to_cpu(revinfo.deviceid);
ri->radiorev = le32_to_cpu(revinfo.radiorev);
ri->chiprev = le32_to_cpu(revinfo.chiprev);
ri->corerev = le32_to_cpu(revinfo.corerev);
ri->boardid = le32_to_cpu(revinfo.boardid);
ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
ri->boardrev = le32_to_cpu(revinfo.boardrev);
ri->driverrev = le32_to_cpu(revinfo.driverrev);
ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
ri->bus = le32_to_cpu(revinfo.bus);
ri->chipnum = le32_to_cpu(revinfo.chipnum);
ri->phytype = le32_to_cpu(revinfo.phytype);
ri->phyrev = le32_to_cpu(revinfo.phyrev);
ri->anarev = le32_to_cpu(revinfo.anarev);
ri->chippkg = le32_to_cpu(revinfo.chippkg);
ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
}
ri->result = err;
/* query for 'ver' to get version info from firmware */
memset(buf, 0, sizeof(buf));
strcpy(buf, "ver");

View file

@ -37,6 +37,8 @@ struct brcmf_commonring {
unsigned long flags;
bool inited;
bool was_full;
atomic_t outstanding_tx;
};

View file

@ -197,7 +197,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
/* Can the device send data? */
if (drvr->bus_if->state != BRCMF_BUS_DATA) {
if (drvr->bus_if->state != BRCMF_BUS_UP) {
brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
netif_stop_queue(ndev);
dev_kfree_skb(skb);
@ -601,9 +601,12 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
char drev[BRCMU_DOTREV_LEN] = "n/a";
if (drvr->revinfo.result == 0)
brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
snprintf(info->version, sizeof(info->version), "n/a");
strlcpy(info->version, drev, sizeof(info->version));
strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
sizeof(info->bus_info));
@ -637,7 +640,7 @@ static int brcmf_netdev_open(struct net_device *ndev)
brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
/* If bus is not ready, can't continue */
if (bus_if->state != BRCMF_BUS_DATA) {
if (bus_if->state != BRCMF_BUS_UP) {
brcmf_err("failed bus is not ready\n");
return -EAGAIN;
}
@ -964,13 +967,20 @@ int brcmf_bus_start(struct device *dev)
p2p_ifp = NULL;
/* signal bus ready */
brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA);
brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
/* Bus is ready, do any initialization */
ret = brcmf_c_preinit_dcmds(ifp);
if (ret < 0)
goto fail;
/* assure we have chipid before feature attach */
if (!bus_if->chip) {
bus_if->chip = drvr->revinfo.chipnum;
bus_if->chiprev = drvr->revinfo.chiprev;
brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
bus_if->chip, bus_if->chip, bus_if->chiprev);
}
brcmf_feat_attach(drvr);
ret = brcmf_fws_init(drvr);
@ -1106,6 +1116,27 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
return !err;
}
void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
{
struct brcmf_pub *drvr = bus->drvr;
struct net_device *ndev;
int ifidx;
brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
bus->state = state;
if (state == BRCMF_BUS_UP) {
for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
if ((drvr->iflist[ifidx]) &&
(drvr->iflist[ifidx]->ndev)) {
ndev = drvr->iflist[ifidx]->ndev;
if (netif_queue_stopped(ndev))
netif_wake_queue(ndev);
}
}
}
}
static void brcmf_driver_register(struct work_struct *work)
{
#ifdef CONFIG_BRCMFMAC_SDIO

View file

@ -71,6 +71,35 @@ struct brcmf_proto; /* device communication protocol info */
struct brcmf_cfg80211_dev; /* cfg80211 device info */
struct brcmf_fws_info; /* firmware signalling info */
/*
* struct brcmf_rev_info
*
* The result field stores the error code of the
* revision info request from firmware. For the
* other fields see struct brcmf_rev_info_le in
* fwil_types.h
*/
struct brcmf_rev_info {
int result;
u32 vendorid;
u32 deviceid;
u32 radiorev;
u32 chiprev;
u32 corerev;
u32 boardid;
u32 boardvendor;
u32 boardrev;
u32 driverrev;
u32 ucoderev;
u32 bus;
u32 chipnum;
u32 phytype;
u32 phyrev;
u32 anarev;
u32 chippkg;
u32 nvramrev;
};
/* Common structure for module and instance linkage */
struct brcmf_pub {
/* Linkage ponters */
@ -104,6 +133,7 @@ struct brcmf_pub {
u32 feat_flags;
u32 chip_quirks;
struct brcmf_rev_info revinfo;
#ifdef DEBUG
struct dentry *dbgfs_dir;
#endif

View file

@ -103,7 +103,11 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
c = nvp->fwnv->data[nvp->pos];
if (c == '=') {
st = VALUE;
/* ignore RAW1 by treating as comment */
if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0)
st = COMMENT;
else
st = VALUE;
} else if (!is_nvram_char(c)) {
brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
nvp->line, nvp->column);

View file

@ -109,7 +109,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
struct brcmf_pub *drvr = ifp->drvr;
s32 err;
if (drvr->bus_if->state != BRCMF_BUS_DATA) {
if (drvr->bus_if->state != BRCMF_BUS_UP) {
brcmf_err("bus is down. we have nothing to do.\n");
return -EIO;
}

View file

@ -59,6 +59,7 @@
#define BRCMF_C_SET_COUNTRY 84
#define BRCMF_C_GET_PM 85
#define BRCMF_C_SET_PM 86
#define BRCMF_C_GET_REVINFO 98
#define BRCMF_C_GET_CURR_RATESET 114
#define BRCMF_C_GET_AP 117
#define BRCMF_C_SET_AP 118

View file

@ -539,4 +539,45 @@ struct brcmf_fil_country_le {
char ccode[BRCMF_COUNTRY_BUF_SZ];
};
/**
* struct brcmf_rev_info_le - device revision info.
*
* @vendorid: PCI vendor id.
* @deviceid: device id of chip.
* @radiorev: radio revision.
* @chiprev: chip revision.
* @corerev: core revision.
* @boardid: board identifier (usu. PCI sub-device id).
* @boardvendor: board vendor (usu. PCI sub-vendor id).
* @boardrev: board revision.
* @driverrev: driver version.
* @ucoderev: microcode version.
* @bus: bus type.
* @chipnum: chip number.
* @phytype: phy type.
* @phyrev: phy revision.
* @anarev: anacore rev.
* @chippkg: chip package info.
* @nvramrev: nvram revision number.
*/
struct brcmf_rev_info_le {
__le32 vendorid;
__le32 deviceid;
__le32 radiorev;
__le32 chiprev;
__le32 corerev;
__le32 boardid;
__le32 boardvendor;
__le32 boardrev;
__le32 driverrev;
__le32 ucoderev;
__le32 bus;
__le32 chipnum;
__le32 phytype;
__le32 phyrev;
__le32 anarev;
__le32 chippkg;
__le32 nvramrev;
};
#endif /* FWIL_TYPES_H_ */

View file

@ -73,6 +73,8 @@
#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32
#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96
#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64
#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
struct msgbuf_common_hdr {
u8 msgtype;
@ -749,6 +751,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
tx_msghdr->metadata_buf_len = 0;
tx_msghdr->metadata_buf_addr.high_addr = 0;
tx_msghdr->metadata_buf_addr.low_addr = 0;
atomic_inc(&commonring->outstanding_tx);
if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
brcmf_commonring_write_complete(commonring);
count = 0;
@ -773,10 +776,16 @@ static void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
}
static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid)
static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
bool force)
{
struct brcmf_commonring *commonring;
set_bit(flowid, msgbuf->flow_map);
queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
commonring = msgbuf->flowrings[flowid];
if ((force) || (atomic_read(&commonring->outstanding_tx) <
BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
return 0;
}
@ -797,7 +806,7 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx,
return -ENOMEM;
}
brcmf_flowring_enqueue(flow, flowid, skb);
brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false);
return 0;
}
@ -854,6 +863,7 @@ brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
static void
brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
{
struct brcmf_commonring *commonring;
struct msgbuf_tx_status *tx_status;
u32 idx;
struct sk_buff *skb;
@ -871,6 +881,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
}
set_bit(flowid, msgbuf->txstatus_done_map);
commonring = msgbuf->flowrings[flowid];
atomic_dec(&commonring->outstanding_tx);
brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
}
@ -1181,7 +1193,7 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
brcmf_flowring_open(msgbuf->flow, flowid);
brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
}
@ -1280,8 +1292,10 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
struct brcmf_commonring *commonring;
void *buf;
u32 flowid;
int qlen;
buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
brcmf_msgbuf_process_rx(msgbuf, buf);
@ -1293,8 +1307,12 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
for_each_set_bit(flowid, msgbuf->txstatus_done_map,
msgbuf->nrof_flowrings) {
clear_bit(flowid, msgbuf->txstatus_done_map);
if (brcmf_flowring_qlen(msgbuf->flow, flowid))
brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
commonring = msgbuf->flowrings[flowid];
qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
((qlen) && (atomic_read(&commonring->outstanding_tx) <
BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
}
return 0;

View file

@ -1828,7 +1828,7 @@ static int brcmf_pcie_resume(struct pci_dev *pdev)
goto cleanup;
brcmf_dbg(PCIE, "Hot resume, continue....\n");
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
brcmf_bus_change_state(bus, BRCMF_BUS_DATA);
brcmf_bus_change_state(bus, BRCMF_BUS_UP);
brcmf_pcie_intr_enable(devinfo);
return 0;
}

View file

@ -44,7 +44,8 @@
#include "chip.h"
#include "firmware.h"
#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
#define CTL_DONE_TIMEOUT 2000 /* In milli second */
#ifdef DEBUG
@ -495,9 +496,9 @@ struct brcmf_sdio {
u8 *ctrl_frame_buf;
u16 ctrl_frame_len;
bool ctrl_frame_stat;
int ctrl_frame_err;
spinlock_t txq_lock; /* protect bus->txq */
struct semaphore tx_seq_lock; /* protect bus->tx_seq */
wait_queue_head_t ctrl_wait;
wait_queue_head_t dcmd_resp_wait;
@ -514,7 +515,6 @@ struct brcmf_sdio {
bool txoff; /* Transmit flow-controlled */
struct brcmf_sdio_count sdcnt;
bool sr_enabled; /* SaveRestore enabled */
bool sleeping; /* SDIO bus sleeping */
u8 tx_hdrlen; /* sdio bus header length for tx packet */
bool txglom; /* host tx glomming enable flag */
@ -1013,12 +1013,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
(sleep ? "SLEEP" : "WAKE"),
(bus->sleeping ? "SLEEP" : "WAKE"));
(bus->sdiodev->sleeping ? "SLEEP" : "WAKE"));
/* If SR is enabled control bus state with KSO */
if (bus->sr_enabled) {
/* Done if we're already in the requested state */
if (sleep == bus->sleeping)
if (sleep == bus->sdiodev->sleeping)
goto end;
/* Going to sleep */
@ -1050,12 +1050,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
bus->idlecount = 0;
err = brcmf_sdio_kso_control(bus, true);
}
if (!err) {
/* Change state */
bus->sleeping = sleep;
brcmf_dbg(SDIO, "new state %s\n",
(sleep ? "SLEEP" : "WAKE"));
} else {
if (err) {
brcmf_err("error while changing bus sleep state %d\n",
err);
goto done;
@ -1070,6 +1065,11 @@ end:
} else {
brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
}
bus->sdiodev->sleeping = sleep;
if (sleep)
wake_up(&bus->sdiodev->idle_wait);
brcmf_dbg(SDIO, "new state %s\n",
(sleep ? "SLEEP" : "WAKE"));
done:
brcmf_dbg(SDIO, "Exit: err=%d\n", err);
return err;
@ -1909,7 +1909,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
bus->rxpending = true;
for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
!bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if);
!bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_STATE_DATA;
rd->seq_num++, rxleft--) {
/* Handle glomming separately */
@ -2376,8 +2376,6 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
/* Send frames until the limit or some other event */
for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
pkt_num = 1;
if (down_interruptible(&bus->tx_seq_lock))
return cnt;
if (bus->txglom)
pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
bus->sdiodev->txglomsz);
@ -2393,13 +2391,10 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
__skb_queue_tail(&pktq, pkt);
}
spin_unlock_bh(&bus->txq_lock);
if (i == 0) {
up(&bus->tx_seq_lock);
if (i == 0)
break;
}
ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
up(&bus->tx_seq_lock);
cnt += i;
@ -2420,7 +2415,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
}
/* Deflow-control stack if needed */
if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) &&
if ((bus->sdiodev->state == BRCMF_STATE_DATA) &&
bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
bus->txoff = false;
brcmf_txflowblock(bus->sdiodev->dev, false);
@ -2508,7 +2503,7 @@ static void brcmf_sdio_bus_stop(struct device *dev)
bus->watchdog_tsk = NULL;
}
if (bus_if->state == BRCMF_BUS_DOWN) {
if (sdiodev->state != BRCMF_STATE_NOMEDIUM) {
sdio_claim_host(sdiodev->func[1]);
/* Enable clock for device interrupts */
@ -2543,8 +2538,7 @@ static void brcmf_sdio_bus_stop(struct device *dev)
brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
/* Clear any held glomming stuff */
if (bus->glomd)
brcmu_pkt_buf_free_skb(bus->glomd);
brcmu_pkt_buf_free_skb(bus->glomd);
brcmf_sdio_free_glom(bus);
/* Clear rx control and wake any waiters */
@ -2609,6 +2603,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
return ret;
}
static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev)
{
#ifdef CONFIG_PM_SLEEP
int retry;
/* Wait for possible resume to complete */
retry = 0;
while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50))
msleep(20);
if (atomic_read(&sdiodev->suspend))
return -EIO;
#endif
return 0;
}
static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
{
u32 newstatus = 0;
@ -2619,6 +2628,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
brcmf_dbg(TRACE, "Enter\n");
if (brcmf_sdio_pm_resume_wait(bus->sdiodev))
return;
sdio_claim_host(bus->sdiodev->func[1]);
/* If waiting for HTAVAIL, check status */
@ -2725,17 +2737,14 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
brcmf_sdio_clrintr(bus);
if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
(down_interruptible(&bus->tx_seq_lock) == 0)) {
if (data_ok(bus)) {
sdio_claim_host(bus->sdiodev->func[1]);
err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
bus->ctrl_frame_len);
sdio_release_host(bus->sdiodev->func[1]);
bus->ctrl_frame_stat = false;
brcmf_sdio_wait_event_wakeup(bus);
}
up(&bus->tx_seq_lock);
data_ok(bus)) {
sdio_claim_host(bus->sdiodev->func[1]);
err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
bus->ctrl_frame_len);
sdio_release_host(bus->sdiodev->func[1]);
bus->ctrl_frame_err = err;
bus->ctrl_frame_stat = false;
brcmf_sdio_wait_event_wakeup(bus);
}
/* Send queued frames (limit 1 if rx may still be pending) */
if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
@ -2746,7 +2755,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
brcmf_sdio_sendfromq(bus, framecnt);
}
if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) {
if ((bus->sdiodev->state != BRCMF_STATE_DATA) || (err != 0)) {
brcmf_err("failed backplane access over SDIO, halting operation\n");
atomic_set(&bus->intstatus, 0);
} else if (atomic_read(&bus->intstatus) ||
@ -2947,43 +2956,30 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
int ret = -1;
int ret;
brcmf_dbg(TRACE, "Enter\n");
if (down_interruptible(&bus->tx_seq_lock))
return -EINTR;
if (!data_ok(bus)) {
brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
bus->tx_max, bus->tx_seq);
up(&bus->tx_seq_lock);
/* Send from dpc */
bus->ctrl_frame_buf = msg;
bus->ctrl_frame_len = msglen;
bus->ctrl_frame_stat = true;
wait_event_interruptible_timeout(bus->ctrl_wait,
!bus->ctrl_frame_stat,
msecs_to_jiffies(2000));
if (!bus->ctrl_frame_stat) {
brcmf_dbg(SDIO, "ctrl_frame_stat == false\n");
ret = 0;
} else {
brcmf_dbg(SDIO, "ctrl_frame_stat == true\n");
bus->ctrl_frame_stat = false;
if (down_interruptible(&bus->tx_seq_lock))
return -EINTR;
ret = -1;
}
/* Send from dpc */
bus->ctrl_frame_buf = msg;
bus->ctrl_frame_len = msglen;
bus->ctrl_frame_stat = true;
if (atomic_read(&bus->dpc_tskcnt) == 0) {
atomic_inc(&bus->dpc_tskcnt);
queue_work(bus->brcmf_wq, &bus->datawork);
}
if (ret == -1) {
sdio_claim_host(bus->sdiodev->func[1]);
brcmf_sdio_bus_sleep(bus, false, false);
ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen);
sdio_release_host(bus->sdiodev->func[1]);
up(&bus->tx_seq_lock);
wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
msecs_to_jiffies(CTL_DONE_TIMEOUT));
if (!bus->ctrl_frame_stat) {
brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
bus->ctrl_frame_err);
ret = bus->ctrl_frame_err;
} else {
brcmf_dbg(SDIO, "ctrl_frame timeout\n");
bus->ctrl_frame_stat = false;
ret = -ETIMEDOUT;
}
if (ret)
@ -2991,7 +2987,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
else
bus->sdcnt.tx_ctlpkts++;
return ret ? -EIO : 0;
return ret;
}
#ifdef DEBUG
@ -3414,8 +3410,8 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
goto err;
}
/* Allow HT Clock now that the ARM is running. */
brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD);
/* Allow full data communication using DPC from now on. */
bus->sdiodev->state = BRCMF_STATE_DATA;
bcmerror = 0;
err:
@ -3561,7 +3557,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus)
return;
}
if (!brcmf_bus_ready(bus->sdiodev->bus_if)) {
if (bus->sdiodev->state != BRCMF_STATE_DATA) {
brcmf_err("bus is down. we have nothing to do\n");
return;
}
@ -3584,10 +3580,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus)
static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
{
#ifdef DEBUG
struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
#endif /* DEBUG */
brcmf_dbg(TIMER, "Enter\n");
/* Poll period: check device if appropriate. */
@ -3631,7 +3623,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
}
#ifdef DEBUG
/* Poll for console output periodically */
if (bus_if && bus_if->state == BRCMF_BUS_DATA &&
if (bus->sdiodev->state == BRCMF_STATE_DATA &&
bus->console_interval != 0) {
bus->console.count += BRCMF_WD_POLL_MS;
if (bus->console.count >= bus->console_interval) {
@ -3872,11 +3864,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
goto fail;
}
/* SDIO register access works so moving
* state from UNKNOWN to DOWN.
*/
brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN);
bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
if (IS_ERR(bus->ci)) {
brcmf_err("brcmf_chip_attach failed!\n");
@ -4010,18 +3997,16 @@ static void brcmf_sdio_firmware_callback(struct device *dev,
brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
/* try to download image and nvram to the dongle */
if (bus_if->state == BRCMF_BUS_DOWN) {
bus->alp_only = true;
err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
if (err)
goto fail;
bus->alp_only = false;
}
if (!bus_if->drvr)
return;
/* try to download image and nvram to the dongle */
bus->alp_only = true;
err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
if (err)
goto fail;
bus->alp_only = false;
/* Start the watchdog timer */
bus->sdcnt.tickcnt = 0;
brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
@ -4147,7 +4132,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
spin_lock_init(&bus->rxctl_lock);
spin_lock_init(&bus->txq_lock);
sema_init(&bus->tx_seq_lock, 1);
init_waitqueue_head(&bus->ctrl_wait);
init_waitqueue_head(&bus->dcmd_resp_wait);
@ -4218,7 +4202,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->idleclock = BRCMF_IDLE_ACTIVE;
/* SR state */
bus->sleeping = false;
bus->sr_enabled = false;
brcmf_sdio_debugfs_create(bus);
@ -4259,7 +4242,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
destroy_workqueue(bus->brcmf_wq);
if (bus->ci) {
if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
if (bus->sdiodev->state != BRCMF_STATE_NOMEDIUM) {
sdio_claim_host(bus->sdiodev->func[1]);
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
/* Leave the device in state where it is
@ -4294,7 +4277,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
}
/* don't start the wd until fw is loaded */
if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA)
if (bus->sdiodev->state != BRCMF_STATE_DATA)
return;
if (wdtick) {

View file

@ -155,6 +155,13 @@
/* watchdog polling interval in ms */
#define BRCMF_WD_POLL_MS 10
/* The state of the bus */
enum brcmf_sdio_state {
BRCMF_STATE_DOWN, /* Device available, still initialising */
BRCMF_STATE_DATA, /* Ready for data transfers, DPC enabled */
BRCMF_STATE_NOMEDIUM /* No medium access to dongle possible */
};
struct brcmf_sdreg {
int func;
int offset;
@ -169,8 +176,8 @@ struct brcmf_sdio_dev {
u32 sbwad; /* Save backplane window address */
struct brcmf_sdio *bus;
atomic_t suspend; /* suspend flag */
wait_queue_head_t request_word_wait;
wait_queue_head_t request_buffer_wait;
bool sleeping;
wait_queue_head_t idle_wait;
struct device *dev;
struct brcmf_bus *bus_if;
struct brcmfmac_sdio_platform_data *pdata;
@ -187,6 +194,7 @@ struct brcmf_sdio_dev {
char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
bool wowl_enabled;
enum brcmf_sdio_state state;
};
/* sdio core registers */

View file

@ -421,7 +421,7 @@ fail:
brcmf_err("fail!\n");
while (!list_empty(q)) {
req = list_entry(q->next, struct brcmf_usbreq, list);
if (req && req->urb)
if (req)
usb_free_urb(req->urb);
list_del(q->next);
}
@ -576,7 +576,7 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN);
} else if (state == BRCMFMAC_USB_STATE_UP) {
brcmf_dbg(USB, "DBUS is up\n");
brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA);
brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP);
} else {
brcmf_dbg(USB, "DBUS current state=%d\n", state);
}
@ -1263,6 +1263,8 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
ret = brcmf_usb_bus_setup(devinfo);
if (ret)
goto fail;
/* we are done */
return 0;
}
bus->chip = bus_pub->devid;
bus->chiprev = bus_pub->chiprev;

View file

@ -78,7 +78,7 @@ int brcms_debugfs_hardware_read(struct seq_file *s, void *data)
struct brcms_hardware *hw = drvr->wlc->hw;
struct bcma_device *core = hw->d11core;
struct bcma_bus *bus = core->bus;
char boardrev[10];
char boardrev[BRCMU_BOARDREV_LEN];
seq_printf(s, "chipnum 0x%x\n"
"chiprev 0x%x\n"

View file

@ -267,15 +267,43 @@ char *brcmu_boardrev_str(u32 brev, char *buf)
char c;
if (brev < 0x100) {
snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d",
(brev & 0xf0) >> 4, brev & 0xf);
} else {
c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
snprintf(buf, 8, "%c%03x", c, brev & 0xfff);
snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff);
}
return buf;
}
EXPORT_SYMBOL(brcmu_boardrev_str);
char *brcmu_dotrev_str(u32 dotrev, char *buf)
{
u8 dotval[4];
if (!dotrev) {
snprintf(buf, BRCMU_DOTREV_LEN, "unknown");
return buf;
}
dotval[0] = (dotrev >> 24) & 0xFF;
dotval[1] = (dotrev >> 16) & 0xFF;
dotval[2] = (dotrev >> 8) & 0xFF;
dotval[3] = dotrev & 0xFF;
if (dotval[3])
snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0],
dotval[1], dotval[2], dotval[3]);
else if (dotval[2])
snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0],
dotval[1], dotval[2]);
else
snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0],
dotval[1]);
return buf;
}
EXPORT_SYMBOL(brcmu_dotrev_str);
#if defined(DEBUG)
/* pretty hex print a pkt buffer chain */
void brcmu_prpkt(const char *msg, struct sk_buff *p0)

View file

@ -218,6 +218,10 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
}
#endif
#define BRCMU_BOARDREV_LEN 8
#define BRCMU_DOTREV_LEN 16
char *brcmu_boardrev_str(u32 brev, char *buf);
char *brcmu_dotrev_str(u32 dotrev, char *buf);
#endif /* _BRCMU_UTILS_H_ */

View file

@ -66,25 +66,31 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
do { \
ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
if (ret < 0) \
goto error; \
goto exit; \
} while (0)
#define APB_WRITE2(reg, val) \
do { \
ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
if (ret < 0) \
goto free_buffer; \
} while (0)
#define APB_READ(reg, val) \
do { \
ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
if (ret < 0) \
goto error; \
goto free_buffer; \
} while (0)
#define REG_WRITE(reg, val) \
do { \
ret = cw1200_reg_write_32(priv, (reg), (val)); \
if (ret < 0) \
goto error; \
goto exit; \
} while (0)
#define REG_READ(reg, val) \
do { \
ret = cw1200_reg_read_32(priv, (reg), &(val)); \
if (ret < 0) \
goto error; \
goto exit; \
} while (0)
switch (priv->hw_revision) {
@ -142,14 +148,14 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
ret = request_firmware(&firmware, fw_path, priv->pdev);
if (ret) {
pr_err("Can't load firmware file %s.\n", fw_path);
goto error;
goto exit;
}
buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
if (!buf) {
pr_err("Can't allocate firmware load buffer.\n");
ret = -ENOMEM;
goto error;
goto firmware_release;
}
/* Check if the bootloader is ready */
@ -163,7 +169,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
if (val32 != DOWNLOAD_I_AM_HERE) {
pr_err("Bootloader is not ready.\n");
ret = -ETIMEDOUT;
goto error;
goto free_buffer;
}
/* Calculcate number of download blocks */
@ -171,7 +177,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
/* Updating the length in Download Ctrl Area */
val32 = firmware->size; /* Explicit cast from size_t to u32 */
APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32);
/* Firmware downloading loop */
for (block = 0; block < num_blocks; block++) {
@ -183,7 +189,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
if (val32 != DOWNLOAD_PENDING) {
pr_err("Bootloader reported error %d.\n", val32);
ret = -EIO;
goto error;
goto free_buffer;
}
/* loop until put - get <= 24K */
@ -198,7 +204,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
pr_err("Timeout waiting for FIFO.\n");
ret = -ETIMEDOUT;
goto error;
goto free_buffer;
}
/* calculate the block size */
@ -220,12 +226,12 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
if (ret < 0) {
pr_err("Can't write firmware block @ %d!\n",
put & (DOWNLOAD_FIFO_SIZE - 1));
goto error;
goto free_buffer;
}
/* update the put register */
put += block_size;
APB_WRITE(DOWNLOAD_PUT_REG, put);
APB_WRITE2(DOWNLOAD_PUT_REG, put);
} /* End of firmware download loop */
/* Wait for the download completion */
@ -238,19 +244,21 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
if (val32 != DOWNLOAD_SUCCESS) {
pr_err("Wait for download completion failed: 0x%.8X\n", val32);
ret = -ETIMEDOUT;
goto error;
goto free_buffer;
} else {
pr_info("Firmware download completed.\n");
ret = 0;
}
error:
free_buffer:
kfree(buf);
if (firmware)
release_firmware(firmware);
firmware_release:
release_firmware(firmware);
exit:
return ret;
#undef APB_WRITE
#undef APB_WRITE2
#undef APB_READ
#undef REG_WRITE
#undef REG_READ

View file

@ -39,9 +39,9 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
cancel_delayed_work_sync(&priv->clear_recent_scan_work);
atomic_set(&priv->scan.in_progress, 1);
atomic_set(&priv->recent_scan, 1);
cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
queue_delayed_work(priv->workqueue, &priv->scan.timeout,
tmo * HZ / 1000);
msecs_to_jiffies(tmo));
ret = wsm_scan(priv, scan);
if (ret) {
atomic_set(&priv->scan.in_progress, 0);
@ -386,8 +386,8 @@ void cw1200_probe_work(struct work_struct *work)
if (down_trylock(&priv->scan.lock)) {
/* Scan is already in progress. Requeue self. */
schedule();
queue_delayed_work(priv->workqueue,
&priv->scan.probe_work, HZ / 10);
queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
msecs_to_jiffies(100));
mutex_unlock(&priv->conf_mutex);
return;
}

View file

@ -145,7 +145,7 @@ static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
if (sta->aid > 0)
ap->sta_aid[sta->aid - 1] = NULL;
if (!sta->ap && sta->u.sta.challenge)
if (!sta->ap)
kfree(sta->u.sta.challenge);
del_timer_sync(&sta->timer);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */

View file

@ -1228,11 +1228,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
if (!iwlwifi_mod_params.wd_disable)
trans_cfg.queue_watchdog_timeout =
priv->cfg->base_params->wd_timeout;
else
trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
trans_cfg.command_names = iwl_dvm_cmd_strings;
trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;

View file

@ -715,7 +715,7 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
fifo = ctx->ac_to_fifo[tid_to_ac[tid]];
iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid,
buf_size, ssn);
buf_size, ssn, 0);
/*
* If the limit is 0, then it wasn't initialised yet,

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