From 81ec3c09ab1bb24e5edf2bd08707982a4e7fb8bd Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 30 Nov 2015 22:29:39 +0530 Subject: [PATCH 01/23] ath10k: remove unnecessary amsdu/ampdu assignment in debugfs The default values of max_num_amsdu / max_num_amdpu is assigned a default value as part of 'ath10k_core_init_firmware_features' Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 39fe4f3350aa0..2bdf5408b0d91 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1139,7 +1139,7 @@ static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file, { struct ath10k *ar = file->private_data; char buf[64]; - u8 amsdu = 3, ampdu = 64; + u8 amsdu, ampdu; unsigned int len; mutex_lock(&ar->conf_mutex); From b057886524be060021e3cfad0ba8458c850330cd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 30 Nov 2015 19:32:01 +0100 Subject: [PATCH 02/23] ath10k: do not use coherent memory for allocated device memory chunks Coherent memory is more expensive to allocate (and constrained on some architectures where it has to be pre-allocated). It is also completely unnecessary, since the host has no reason to even access these allocated memory spaces Signed-off-by: Felix Fietkau Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 61 +++++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 2a44d63a03cde..a7c3d299639b2 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4312,34 +4312,58 @@ void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } -static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, - u32 num_units, u32 unit_len) +static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) { dma_addr_t paddr; - u32 pool_size; + u32 pool_size = 0; int idx = ar->wmi.num_mem_chunks; + void *vaddr = NULL; - pool_size = num_units * round_up(unit_len, 4); + if (ar->wmi.num_mem_chunks == ARRAY_SIZE(ar->wmi.mem_chunks)) + return -ENOMEM; - if (!pool_size) - return -EINVAL; + while (!vaddr && num_units) { + pool_size = num_units * round_up(unit_len, 4); + if (!pool_size) + return -EINVAL; - ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, - pool_size, - &paddr, - GFP_KERNEL); - if (!ar->wmi.mem_chunks[idx].vaddr) { - ath10k_warn(ar, "failed to allocate memory chunk\n"); - return -ENOMEM; + vaddr = kzalloc(pool_size, GFP_KERNEL | __GFP_NOWARN); + if (!vaddr) + num_units /= 2; } - memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); + if (!num_units) + return -ENOMEM; + + paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE); + if (dma_mapping_error(ar->dev, paddr)) { + kfree(vaddr); + return -ENOMEM; + } + ar->wmi.mem_chunks[idx].vaddr = vaddr; ar->wmi.mem_chunks[idx].paddr = paddr; ar->wmi.mem_chunks[idx].len = pool_size; ar->wmi.mem_chunks[idx].req_id = req_id; ar->wmi.num_mem_chunks++; + return num_units; +} + +static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) +{ + int ret; + + while (num_units) { + ret = ath10k_wmi_alloc_chunk(ar, req_id, num_units, unit_len); + if (ret < 0) + return ret; + + num_units -= ret; + } + return 0; } @@ -7717,10 +7741,11 @@ void ath10k_wmi_free_host_mem(struct ath10k *ar) /* free the host memory chunks requested by firmware */ for (i = 0; i < ar->wmi.num_mem_chunks; i++) { - dma_free_coherent(ar->dev, - ar->wmi.mem_chunks[i].len, - ar->wmi.mem_chunks[i].vaddr, - ar->wmi.mem_chunks[i].paddr); + dma_unmap_single(ar->dev, + ar->wmi.mem_chunks[i].paddr, + ar->wmi.mem_chunks[i].len, + DMA_TO_DEVICE); + kfree(ar->wmi.mem_chunks[i].vaddr); } ar->wmi.num_mem_chunks = 0; From 6fa658fd5ab26a769ca7df7bfdd53c212a0ba852 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 31 Oct 2015 13:57:32 +0100 Subject: [PATCH 03/23] ath9k: Simplify and fix eeprom endianness swapping The three eeprom implementations had quite some duplicate code when it came to endianness swapping. Additionally there was a bug in eeprom_4k and eeprom_9287 which prevented the endianness swapping from working correctly, because the swapping code was guarded within an "if (!ath9k_hw_use_flash(ah))". In eeprom_def this check did not exist, so it seems that eeprom_def was the only implementation where endianness swapping worked. This patch takes the duplicate code and moves it from eeprom_* to eeprom.c. The new code is derived from eeprom_def, while taking into account the specifics from the other implementations. Signed-off-by: Martin Blumenstingl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 74 +++++++++++++++++++ drivers/net/wireless/ath/ath9k/eeprom.h | 3 + drivers/net/wireless/ath/ath9k/eeprom_4k.c | 76 ++++---------------- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 68 +++++------------- drivers/net/wireless/ath/ath9k/eeprom_def.c | 61 ++++------------ 5 files changed, 123 insertions(+), 159 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index cc81482c934d6..f8c5065e5f5f8 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -138,6 +138,80 @@ bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) return ret; } +int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size) +{ + u16 magic; + u16 *eepdata; + int i; + struct ath_common *common = ath9k_hw_common(ah); + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + ath_err(common, "Reading Magic # failed\n"); + return -EIO; + } + + if (magic == AR5416_EEPROM_MAGIC) { + *swap_needed = false; + } else if (swab16(magic) == AR5416_EEPROM_MAGIC) { + if (ah->ah_flags & AH_NO_EEP_SWAP) { + ath_info(common, + "Ignoring endianness difference in EEPROM magic bytes.\n"); + + *swap_needed = false; + } else { + *swap_needed = true; + } + } else { + ath_err(common, + "Invalid EEPROM Magic (0x%04x).\n", magic); + return -EINVAL; + } + + eepdata = (u16 *)(&ah->eeprom); + + if (*swap_needed) { + ath_dbg(common, EEPROM, + "EEPROM Endianness is not native.. Changing.\n"); + + for (i = 0; i < size; i++) + eepdata[i] = swab16(eepdata[i]); + } + + return 0; +} + +bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size) +{ + u32 i, sum = 0; + u16 *eepdata = (u16 *)(&ah->eeprom); + struct ath_common *common = ath9k_hw_common(ah); + + for (i = 0; i < size; i++) + sum ^= eepdata[i]; + + if (sum != 0xffff) { + ath_err(common, "Bad EEPROM checksum 0x%x\n", sum); + return false; + } + + return true; +} + +bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (ah->eep_ops->get_eeprom_ver(ah) != version || + ah->eep_ops->get_eeprom_rev(ah) < minrev) { + ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n", + ah->eep_ops->get_eeprom_ver(ah), + ah->eep_ops->get_eeprom_rev(ah)); + return -EINVAL; + } + + return true; +} + void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, u8 *pVpdList, u16 numIntercepts, u8 *pRetVpdList) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 40d4f62d0f163..4465c6566f205 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -664,6 +664,9 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR); bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data); +int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size); +bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size); +bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev); void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, int eep_start_loc, int size); void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 4773da6dc6f2d..5da0826bf1be0 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -177,74 +177,30 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, } #endif - -#undef SIZE_EEPROM_4K - static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) { -#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) - struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u16 *eepdata, temp, magic, magic2; - u32 sum = 0, el; - bool need_swap = false; - int i, addr; + u32 el; + bool need_swap; + int i, err; - - if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - ath_err(common, - "Invalid EEPROM Magic. Endianness mismatch.\n"); - return -EINVAL; - } - } - } - - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_4K); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.map4k.baseEepHeader.length); - else - el = ah->eeprom.map4k.baseEepHeader.length; - - if (el > sizeof(struct ar5416_eeprom_4k)) - el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); + el = swab16(eep->baseEepHeader.length); else - el = el / sizeof(u16); + el = eep->baseEepHeader.length; - eepdata = (u16 *)(&ah->eeprom); - - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_4K); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { u32 integer; u16 word; - ath_dbg(common, EEPROM, - "EEPROM Endianness is not native.. Changing\n"); - word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -283,17 +239,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } return 0; -#undef EEPROM_4K_SIZE } +#undef SIZE_EEPROM_4K + static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 6ca33dfde1fdc..1a019a39eda10 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -177,59 +177,24 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) { - u32 sum = 0, el, integer; - u16 temp, word, magic, magic2, *eepdata; - int i, addr; - bool need_swap = false; + u32 el, integer; + u16 word; + int i, err; + bool need_swap; struct ar9287_eeprom *eep = &ah->eeprom.map9287; - struct ath_common *common = ath9k_hw_common(ah); - - if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - need_swap = true; - eepdata = (u16 *)(&ah->eeprom); - - for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - ath_err(common, - "Invalid EEPROM Magic. Endianness mismatch.\n"); - return -EINVAL; - } - } - } - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_AR9287); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.map9287.baseEepHeader.length); - else - el = ah->eeprom.map9287.baseEepHeader.length; - - if (el > sizeof(struct ar9287_eeprom)) - el = sizeof(struct ar9287_eeprom) / sizeof(u16); + el = swab16(eep->baseEepHeader.length); else - el = el / sizeof(u16); - - eepdata = (u16 *)(&ah->eeprom); + el = eep->baseEepHeader.length; - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_AR9287); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { word = swab16(eep->baseEepHeader.length); @@ -270,16 +235,15 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER - || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR9287_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } return 0; } +#undef SIZE_EEPROM_AR9287 + static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 056f516bf0176..959682f7909c0 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -126,8 +126,6 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) return __ath9k_hw_def_fill_eeprom(ah); } -#undef SIZE_EEPROM_DEF - #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_header *modal_hdr) @@ -257,59 +255,31 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, } #endif - static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ath_common *common = ath9k_hw_common(ah); - u16 *eepdata, temp, magic; - u32 sum = 0, el; - bool need_swap = false; - int i, addr, size; - - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - if (swab16(magic) == AR5416_EEPROM_MAGIC && - !(ah->ah_flags & AH_NO_EEP_SWAP)) { - size = sizeof(struct ar5416_eeprom_def); - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < size / sizeof(u16); addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } + u32 el; + bool need_swap; + int i, err; - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_DEF); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.def.baseEepHeader.length); + el = swab16(eep->baseEepHeader.length); else - el = ah->eeprom.def.baseEepHeader.length; + el = eep->baseEepHeader.length; - if (el > sizeof(struct ar5416_eeprom_def)) - el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); - else - el = el / sizeof(u16); - - eepdata = (u16 *)(&ah->eeprom); - - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_DEF); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { u32 integer, j; u16 word; - ath_dbg(common, EEPROM, - "EEPROM Endianness is not native.. Changing.\n"); - word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -356,12 +326,9 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } /* Enable fixup for AR_AN_TOP2 if necessary */ if ((ah->hw_version.devid == AR9280_DEVID_PCI) && @@ -376,6 +343,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) return 0; } +#undef SIZE_EEPROM_DEF + static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { From 79fd1709b64991c86e5ffef0689b4635874e6504 Mon Sep 17 00:00:00 2001 From: Steve deRosier Date: Wed, 18 Nov 2015 14:51:28 -0800 Subject: [PATCH 04/23] ath6kl: Don't print error message when recv is canceled An error message ath6kl_htc_rxmsg_pending_handler isn't appropate for when the error is ECANCELED. This could be the result of a perfectly appropriate RX cancel due to shutdown or suspend. This allows the right cleanup to continue, but without an alarming error message in this particular case. Signed-off-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_mbox.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index fffb65b3e6526..65c31da43c473 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -2222,8 +2222,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, } if (status) { - ath6kl_err("failed to get pending recv messages: %d\n", - status); + if (status != -ECANCELED) + ath6kl_err("failed to get pending recv messages: %d\n", + status); /* cleanup any packets in sync completion queue */ list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) { From 410d13a6b057f69c58b8bf8ccca8e9cbf57ea710 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 19 Nov 2015 10:04:48 -0500 Subject: [PATCH 05/23] ath5k: fix RTS/CTS by using proper rate flags The rates in the tx control rateset do not have the protection flags applied, so RTS/CTS would never get enabled if requested. Fix by using the rate flags in the rates returned by ieee80211_get_tx_rates(). Signed-off-by: Bob Copeland Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath5k/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 342563a3706f4..3d946d8b2db24 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -767,7 +767,7 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (info->flags & IEEE80211_TX_CTL_NO_ACK) flags |= AR5K_TXDESC_NOACK; - rc_flags = info->control.rates[0].flags; + rc_flags = bf->rates[0].flags; hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0); From 452133a717dd13b57a99defb791d25c568483f6b Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Tue, 24 Nov 2015 09:30:15 +0200 Subject: [PATCH 06/23] wil6210: prevent external wmi commands during suspend flow In __wmi_send we check if fw is ready at the beginning of the function. While we wait for the completion of the previous command, system suspend can be invoked and reset the HW, causing __wmi_send to read from HW registers while it is not ready. Taking the wmi_mutex in the reset flow when setting the FW ready bit to zero will prevent the above race condition. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 4 +++- drivers/net/wireless/ath/wil6210/wmi.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 48687f128dc61..09b4daebab9d9 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -781,8 +781,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil_bcast_fini(wil); - /* prevent NAPI from being scheduled */ + /* prevent NAPI from being scheduled and prevent wmi commands */ + mutex_lock(&wil->wmi_mutex); bitmap_zero(wil->status, wil_status_last); + mutex_unlock(&wil->wmi_mutex); if (wil->scan_request) { wil_dbg_misc(wil, "Abort scan_request 0x%p\n", diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6ed26baca0e51..e3ea74cdd4aae 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -228,6 +228,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { + if (!test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "WMI: cannot send command while FW not ready\n"); + return -EAGAIN; + } r->tail = wil_r(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.tail)); if (next_head != r->tail) From 58bb9ca84c2013615069b94940846b274fc8e2c7 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:06 +0100 Subject: [PATCH 07/23] ath9k: add debug messages to aggr/chanctx funcs Add/extend debug messages when chanctx used. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 11 ++++++++--- drivers/net/wireless/ath/ath9k/hw.c | 8 ++++---- drivers/net/wireless/ath/ath9k/xmit.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 90f5773a1a614..35802c9d2d630 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -403,7 +403,7 @@ static void ath_chanctx_offchannel_noa(struct ath_softc *sc, avp->offchannel_duration = sc->sched.offchannel_duration; ath_dbg(common, CHAN_CTX, - "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n", + "offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n", avp->offchannel_duration, avp->offchannel_start, avp->noa_index); @@ -443,7 +443,7 @@ static void ath_chanctx_set_periodic_noa(struct ath_softc *sc, avp->periodic_noa = true; ath_dbg(common, CHAN_CTX, - "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n", + "noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", avp->noa_duration, avp->noa_start, avp->noa_index, @@ -464,7 +464,7 @@ static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc, avp->noa_duration = duration + sc->sched.channel_switch_time; ath_dbg(common, CHAN_CTX, - "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n", + "oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", avp->noa_duration, avp->noa_start, avp->noa_index, @@ -1401,6 +1401,7 @@ void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx) static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; s32 tsf, target_tsf; @@ -1418,6 +1419,10 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) target_tsf = tsf + ATH_P2P_PS_STOP_TIME; + ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n", + __func__, avp->noa.absent, tsf, target_tsf, + (target_tsf - tsf) / 1000); + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 41382f89abe1d..2e252af0b26a6 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2299,10 +2299,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, else nextTbtt = bs->bs_nexttbtt; - ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim); - ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt); - ath_dbg(common, BEACON, "beacon period %d\n", beaconintval); - ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod); + ath_dbg(common, BEACON, "next DTIM %u\n", bs->bs_nextdtim); + ath_dbg(common, BEACON, "next beacon %u\n", nextTbtt); + ath_dbg(common, BEACON, "beacon period %u\n", beaconintval); + ath_dbg(common, BEACON, "DTIM period %u\n", dtimperiod); ENABLE_REGWRITE_BUFFER(ah); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3e3dac3d70604..26698a6fbd1c4 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1473,11 +1473,14 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *txtid; struct ath_txq *txq; struct ath_node *an; u8 density; + ath_dbg(common, XMIT, "%s called\n", __func__); + an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); txq = txtid->txq; @@ -1512,10 +1515,13 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); struct ath_txq *txq = txtid->txq; + ath_dbg(common, XMIT, "%s called\n", __func__); + ath_txq_lock(sc, txq); txtid->active = false; ath_tx_flush_tid(sc, txtid); @@ -1526,11 +1532,14 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_txq *txq; bool buffered; int tidno; + ath_dbg(common, XMIT, "%s called\n", __func__); + for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { @@ -1555,10 +1564,13 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_txq *txq; int tidno; + ath_dbg(common, XMIT, "%s called\n", __func__); + for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { @@ -1579,10 +1591,13 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tidno) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_node *an; struct ath_txq *txq; + ath_dbg(common, XMIT, "%s called\n", __func__); + an = (struct ath_node *)sta->drv_priv; tid = ATH_AN_2_TID(an, tidno); txq = tid->txq; From 2f985539b98b01ddcb06c214f3c6b81f40e083e4 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:07 +0100 Subject: [PATCH 08/23] ath9k: print real timer value In case of low HZ before this patch we saw wrong values in debug message. Print real timeout value. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 35802c9d2d630..dddaaeac9433b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -356,14 +356,16 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; + unsigned long timeout; ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); tsf_time -= ath9k_hw_gettsf32(ah); - tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; - mod_timer(&sc->sched.timer, jiffies + tsf_time); + timeout = msecs_to_jiffies(tsf_time / 1000) + 1; + mod_timer(&sc->sched.timer, jiffies + timeout); ath_dbg(common, CHAN_CTX, - "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time)); + "Setup chanctx timer with timeout: %d (%d) ms\n", + tsf_time / 1000, jiffies_to_msecs(timeout)); } static void ath_chanctx_handle_bmiss(struct ath_softc *sc, From 60337ed86306c1f75d2f6744c6ff53dfd39eacc0 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:08 +0100 Subject: [PATCH 09/23] ath9k: queue null frames in case of MCC While mac80211 using null frames when connection polling, we should queue this frames while NOA could be there, and AP, P2P_GO could be not present. Without this patch, with no traffic we often saw disconnections while we try to send nullfunc when AP/GO wasn't present. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 26698a6fbd1c4..82fc76f5b2a98 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2331,6 +2331,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, queue = ieee80211_is_data_present(hdr->frame_control); + /* If chanctx, queue all null frames while NOA could be there */ + if (ath9k_is_chanctx_enabled() && + ieee80211_is_nullfunc(hdr->frame_control) && + !txctl->force_channel) + queue = true; + /* Force queueing of all frames that belong to a virtual interface on * a different channel context, to ensure that they are sent on the * correct channel. From b77b59ae8acd854d3b18ea3cbd85646922083b0b Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:09 +0100 Subject: [PATCH 10/23] ath9k: P2P_CLIENT, send frames after 1ms AP/GO will aprear AP/GO will aprear after NOA, wait 1ms to be sure AP could receive/answer this frames. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index dddaaeac9433b..5640e88c6569c 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1417,6 +1417,8 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) target_tsf = avp->noa.next_tsf; if (!avp->noa.absent) target_tsf -= ATH_P2P_PS_STOP_TIME; + else + target_tsf += ATH_P2P_PS_STOP_TIME; if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) target_tsf = tsf + ATH_P2P_PS_STOP_TIME; @@ -1543,6 +1545,8 @@ void ath9k_p2p_ps_timer(void *priv) tsf = ath9k_hw_gettsf32(sc->sc_ah); if (!avp->noa.absent) tsf += ATH_P2P_PS_STOP_TIME; + else + tsf -= ATH_P2P_PS_STOP_TIME; if (!avp->noa.has_next_tsf || avp->noa.next_tsf - tsf > BIT(31)) From 631c45f41957032056568b57b185c214dcebbe84 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:10 +0100 Subject: [PATCH 11/23] ath9k: use u32 when calculate tsf Use u32 while ath9k_hw_gettsf32() and ath9k_hw_gen_timer_start() require u32. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 5640e88c6569c..28bbbef7cabda 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1405,7 +1405,7 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; - s32 tsf, target_tsf; + u32 tsf, target_tsf; if (!avp || !avp->noa.has_next_tsf) return; @@ -1427,7 +1427,7 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) __func__, avp->noa.absent, tsf, target_tsf, (target_tsf - tsf) / 1000); - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000); } static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) From c1b7bea038f00e710e738d8a8a76dc87ceb013f8 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:11 +0100 Subject: [PATCH 12/23] ath9k: setup correct skb priority for nullfunc After queue nullfunc for MCC case, we hit WARN_ON in xmit.c:2398 while skb priority wasn't set. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 28bbbef7cabda..2afb8dabf39a9 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1101,6 +1101,7 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + skb->priority = 7; skb_set_queue_mapping(skb, IEEE80211_AC_VO); if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { dev_kfree_skb_any(skb); From 3edbf0ba0494ad4c576763e733ad0da3bed2f621 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:12 +0100 Subject: [PATCH 13/23] ath9k: MCC enable Opportunistic Power Save When adding NOA attr enable Opportunistic Power Save. Before we calculate ctwindow but didn't enable oppps. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 2afb8dabf39a9..31b4d0efcf6bd 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1505,6 +1505,8 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, noa->index = avp->noa_index; noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp); + if (noa->oppps_ctwindow) + noa->oppps_ctwindow |= BIT(7); if (avp->noa_duration) { if (avp->periodic_noa) { From b10b7fb31a217084de0cf834d0e72c24d9710015 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:13 +0100 Subject: [PATCH 14/23] ath9k: P2P_CLIENT, get/set NOA correctly In case we get BSS_CHANGED_P2P_PS early, from mac80211, we didn't set NOA timer correctly, while p2p_ps_vif was NULL. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 31b4d0efcf6bd..5a1ede67b0708 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1443,6 +1443,10 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) return; sc->p2p_ps_vif = avp; + + if (sc->ps_flags & PS_BEACON_SYNC) + return; + tsf = ath9k_hw_gettsf32(sc->sc_ah); ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); ath9k_update_p2p_ps_timer(sc, avp); @@ -1585,8 +1589,7 @@ void ath9k_p2p_bss_info_changed(struct ath_softc *sc, spin_lock_bh(&sc->sc_pcu_lock); spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (!(sc->ps_flags & PS_BEACON_SYNC)) - ath9k_update_p2p_ps(sc, vif); + ath9k_update_p2p_ps(sc, vif); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); spin_unlock_bh(&sc->sc_pcu_lock); } From a64d876ef7dc33a7e3c52b06db2c2a55ff8ac785 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:15 +0100 Subject: [PATCH 15/23] ath9k: MCC, add NOA also in case of an AP In case of MCC and AP interface, add also NOA attr that will inform stations about absence of an AP. There is a chance that some stations will handle this NOA attr correctly and will know exactly when AP is present/absent. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/beacon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index f50a6bc5d06ee..5cf0cd7cb2d19 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -148,7 +148,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, ath_assign_seq(common, skb); - if (vif->p2p) + /* Always assign NOA attr when MCC enabled */ + if (ath9k_is_chanctx_enabled()) ath9k_beacon_add_noa(sc, avp, skb); bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, From 02edab5b5f82fcb35696b2606e840a3ec87d7954 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:17 +0100 Subject: [PATCH 16/23] ath9k: MCC, print time elapsed between events This is useful for MCC debugging and bug fixing. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/channel.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b42f4a963ef40..acc21f60f36b7 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -981,6 +981,7 @@ struct ath_softc { struct ath_offchannel offchannel; struct ath_chanctx *next_chan; struct completion go_beacon; + struct timespec last_event_time; #endif unsigned long driver_data; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 5a1ede67b0708..50e614b915f1b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -226,6 +226,20 @@ static const char *chanctx_state_string(enum ath_chanctx_state state) } } +static const u32 chanctx_event_delta(struct ath_softc *sc) +{ + u64 ms; + struct timespec ts, *old; + + getrawmonotonic(&ts); + old = &sc->last_event_time; + ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000; + sc->last_event_time = ts; + + return (u32)ms; +} + void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -489,10 +503,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, spin_lock_bh(&sc->chan_lock); - ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n", + ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n", sc->cur_chan->chandef.center_freq1, chanctx_event_string(ev), - chanctx_state_string(sc->sched.state)); + chanctx_state_string(sc->sched.state), + chanctx_event_delta(sc)); switch (ev) { case ATH_CHANCTX_EVENT_BEACON_PREPARE: From 05a85a6c42b59cd97d49314ad3d1939039eb9aee Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:18 +0100 Subject: [PATCH 17/23] ath9k: remove ath9k_mod_tsf64_tu Remove ath9k_mod_tsf64_tu() function while we could use div_u64_rem() function. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- .../net/wireless/ath/ath9k/common-beacon.c | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c index 6ad44470d0f25..01d6d3205a654 100644 --- a/drivers/net/wireless/ath/ath9k/common-beacon.c +++ b/drivers/net/wireless/ath/ath9k/common-beacon.c @@ -18,30 +18,16 @@ #define FUDGE 2 -/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ -static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) -{ - u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; - - tsf_mod = tsf & (BIT(10) - 1); - tsf_hi = tsf >> 32; - tsf_lo = ((u32) tsf) >> 10; - - mod_hi = tsf_hi % div_tu; - mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; - - return (mod_lo << 10) | tsf_mod; -} - static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf, unsigned int interval) { - unsigned int offset; + unsigned int offset, divisor; tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); - offset = ath9k_mod_tsf64_tu(tsf, interval); + divisor = TU_TO_USEC(interval); + div_u64_rem(tsf, divisor, &offset); - return (u32) tsf + TU_TO_USEC(interval) - offset; + return (u32) tsf + divisor - offset; } /* From 26ca14d42706ae8b6053244d5aa4e2eb3707045a Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 30 Nov 2015 15:01:47 -0800 Subject: [PATCH 18/23] ath6kl: fix tx/rx antenna reporting for 2x2 devices My previous patch incorrectly reported the antenna for 2x2 devices. It should be a mask instead of a numeric count. This patch fixes that. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 81ac8c59f0ecd..7f3f94fbf1571 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3930,8 +3930,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; - ar->hw.tx_ant = 2; - ar->hw.rx_ant = 2; + ar->hw.tx_ant = 0x3; /* mask, 2 antenna */ + ar->hw.rx_ant = 0x3; } else { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; From efc2b2b50c92c72916608b0e7d74651471fe5dd7 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 30 Nov 2015 15:01:48 -0800 Subject: [PATCH 19/23] ath6kl: add log messages for firmware failure cases. This gives a user a chance to know why a firmware load is failing. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6ae0734f86e0f..da557dc742e62 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -954,8 +954,10 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name); ret = request_firmware(&fw, filename, ar->dev); - if (ret) + if (ret) { + ath6kl_err("Failed request firmware, rv: %d\n", ret); return ret; + } data = fw->data; len = fw->size; @@ -964,11 +966,15 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; if (len < magic_len) { + ath6kl_err("Magic length is invalid, len: %zd magic_len: %zd\n", + len, magic_len); ret = -EINVAL; goto out; } if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { + ath6kl_err("Magic is invalid, magic_len: %zd\n", + magic_len); ret = -EINVAL; goto out; } @@ -987,7 +993,12 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) len -= sizeof(*hdr); data += sizeof(*hdr); + ath6kl_dbg(ATH6KL_DBG_BOOT, "ie-id: %d len: %zd (0x%zx)\n", + ie_id, ie_len, ie_len); + if (len < ie_len) { + ath6kl_err("IE len is invalid, len: %zd ie_len: %zd ie-id: %d\n", + len, ie_len, ie_id); ret = -EINVAL; goto out; } @@ -1008,6 +1019,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_otp == NULL) { + ath6kl_err("fw_otp cannot be allocated\n"); ret = -ENOMEM; goto out; } @@ -1025,6 +1037,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw = vmalloc(ie_len); if (ar->fw == NULL) { + ath6kl_err("fw storage cannot be allocated, len: %zd\n", ie_len); ret = -ENOMEM; goto out; } @@ -1039,6 +1052,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_patch == NULL) { + ath6kl_err("fw_patch storage cannot be allocated, len: %zd\n", ie_len); ret = -ENOMEM; goto out; } From f0b2c30a20299083665f3f14870ff5ce631d32ef Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Thu, 20 Aug 2015 09:21:38 +0800 Subject: [PATCH 20/23] ath9k: fix AR_RX_FILTER for ar9462/ar9565 when rx stopped When rx stopped, AR_RX_FILTER should be cleared, but in ath9k_hw_setrxfilter(), ATH9K_RX_FILTER_CONTROL_WRAPPER will always be set for ar9462/ar9565. Fix this by moving the code in ath9k_hw_setrxfilter() to ath_calcrxfilter(). Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hw.c | 3 --- drivers/net/wireless/ath/ath9k/recv.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2e252af0b26a6..257f46ed4a040 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2761,9 +2761,6 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) ENABLE_REGWRITE_BUFFER(ah); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) - bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER; - REG_WRITE(ah, AR_RX_FILTER, bits); phybits = 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 994daf6c62979..32160fca876a0 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -424,6 +424,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) AR_SREV_9561(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; + if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah)) + rfilt |= ATH9K_RX_FILTER_CONTROL_WRAPPER; + if (ath9k_is_chanctx_enabled() && test_bit(ATH_OP_SCANNING, &common->op_flags)) rfilt |= ATH9K_RX_FILTER_BEACON; From ed14dc0af7ccea867b479feb88efdfe43ca2a0f9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Fri, 9 Oct 2015 17:06:45 +0800 Subject: [PATCH 21/23] ath9k: feeding entropy in kernel from ADC capture This patch is derived from commit 6301566e0b2d ("ath9k: export HW random number generator"), We evaluated the entropy of the ADC data on QCA9531, QCA9561, QCA955x, and AR9340, and it has sufficient quality random data (at least 10 bits and up to 22 bits of min-entropy for a 32-bit value). We conservatively assume the min-entropy is 10 bits out of 32 bits. Thus, ATH9K_RNG_BUF_SIZE is set to 320 (u32) i.e., 1.25 kilobytes of data is inserted to fill up the pool as soon as the entropy counter becomes 896/4096 (set by random.c). Since ADC was not designed to be a dedicated HW RNG, we do not want to bind it to /dev/hwrng framework directly. This patch feeds the entropy directly from the WiFi driver to the input pool. The ADC register output is only used as a seed for the Linux entropy pool. No conditioning is needed, since all the conditioning is performed by the pool itself. Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/Kconfig | 11 +++ drivers/net/wireless/ath/ath9k/Makefile | 1 + drivers/net/wireless/ath/ath9k/ath9k.h | 22 +++++ drivers/net/wireless/ath/ath9k/main.c | 4 + drivers/net/wireless/ath/ath9k/rng.c | 107 ++++++++++++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 drivers/net/wireless/ath/ath9k/rng.c diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index fee0cadb0f5ed..40fa915d6f357 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -176,3 +176,14 @@ config ATH9K_HTC_DEBUGFS depends on ATH9K_HTC && DEBUG_FS ---help--- Say Y, if you need access to ath9k_htc's statistics. + +config ATH9K_HWRNG + bool "Random number generator support" + depends on ATH9K && (HW_RANDOM = y || HW_RANDOM = ATH9K) + default y + ---help--- + This option incorporates the ADC register output as a source of + randomness into Linux entropy pool (/dev/urandom and /dev/random) + + Say Y, feeds the entropy directly from the WiFi driver to the input + pool. diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index ecda613c2d547..76f9dc37500b1 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -15,6 +15,7 @@ ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o ath9k-$(CONFIG_ATH9K_TX99) += tx99.o ath9k-$(CONFIG_ATH9K_WOW) += wow.o +ath9k-$(CONFIG_ATH9K_HWRNG) += rng.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index acc21f60f36b7..5294595da5a7e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "common.h" #include "debug.h" @@ -1041,6 +1042,11 @@ struct ath_softc { u32 wow_intr_before_sleep; bool force_wow; #endif + +#ifdef CONFIG_ATH9K_HWRNG + u32 rng_last; + struct task_struct *rng_task; +#endif }; /********/ @@ -1063,6 +1069,22 @@ static inline int ath9k_tx99_send(struct ath_softc *sc, } #endif /* CONFIG_ATH9K_TX99 */ +/***************************/ +/* Random Number Generator */ +/***************************/ +#ifdef CONFIG_ATH9K_HWRNG +void ath9k_rng_start(struct ath_softc *sc); +void ath9k_rng_stop(struct ath_softc *sc); +#else +static inline void ath9k_rng_start(struct ath_softc *sc) +{ +} + +static inline void ath9k_rng_stop(struct ath_softc *sc) +{ +} +#endif + static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d184e682e6364..c1b33fdcca087 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -739,6 +739,8 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_restore(sc); + ath9k_rng_start(sc); + return 0; } @@ -828,6 +830,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_deinit_channel_context(sc); + ath9k_rng_stop(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c new file mode 100644 index 0000000000000..c9cb2aad7b6f7 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/rng.c @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include +#include + +#include "ath9k.h" +#include "hw.h" +#include "ar9003_phy.h" + +#define ATH9K_RNG_BUF_SIZE 320 +#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 320) >> 10) /* quality: 320/1024 */ + +static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size) +{ + int i, j; + u32 v1, v2, rng_last = sc->rng_last; + struct ath_hw *ah = sc->sc_ah; + + ath9k_ps_wakeup(sc); + + REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1); + REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5); + REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0); + + for (i = 0, j = 0; i < buf_size; i++) { + v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; + v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; + + /* wait for data ready */ + if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff && + v2 != 0xffff) + buf[j++] = (v1 << 16) | v2; + + rng_last = v2; + } + + ath9k_ps_restore(sc); + + sc->rng_last = rng_last; + + return j << 2; +} + +static int ath9k_rng_kthread(void *data) +{ + int bytes_read; + struct ath_softc *sc = data; + u32 *rng_buf; + + rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL); + if (!rng_buf) + goto out; + + while (!kthread_should_stop()) { + bytes_read = ath9k_rng_data_read(sc, rng_buf, + ATH9K_RNG_BUF_SIZE); + if (unlikely(!bytes_read)) { + msleep_interruptible(10); + continue; + } + + /* sleep until entropy bits under write_wakeup_threshold */ + add_hwgenerator_randomness((void *)rng_buf, bytes_read, + ATH9K_RNG_ENTROPY(bytes_read)); + } + + kfree(rng_buf); +out: + sc->rng_task = NULL; + + return 0; +} + +void ath9k_rng_start(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + if (sc->rng_task) + return; + + if (!AR_SREV_9300_20_OR_LATER(ah)) + return; + + sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng"); + if (IS_ERR(sc->rng_task)) + sc->rng_task = NULL; +} + +void ath9k_rng_stop(struct ath_softc *sc) +{ + if (sc->rng_task) + kthread_stop(sc->rng_task); +} From ba38a1718b7386bacbaed81167a68b9a673a0a42 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 8 Dec 2015 23:49:31 +0000 Subject: [PATCH 22/23] ath9k: fix inconsistent indenting on return statement minor change, indenting is one tab out. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 82fc76f5b2a98..fe795fc5288cb 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2915,7 +2915,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, if (skb_headroom(skb) < padsize) { ath_dbg(common, XMIT, "tx99 padding failed\n"); - return -EINVAL; + return -EINVAL; } skb_push(skb, padsize); From 7c97b72a623cf7f7cfede9caed531b2679556cca Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 3 Dec 2015 09:50:55 -0800 Subject: [PATCH 23/23] ath10k: allow Mesh Point to install peer security key Mesh Point requires peer security key install when running in secured mode since it's a type of peer links, otherwise peer link will be removed due to key install failure. MFP feature set is required to run Mesh in secured mode and QCA988X firmware, 10.2.4.70.14-2 and above, is the only one supporting secured Mesh at this moment. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8ae5216fd188c..a4c5c1de38060 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -250,7 +250,8 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP && - arvif->vif->type != NL80211_IFTYPE_ADHOC)) + arvif->vif->type != NL80211_IFTYPE_ADHOC && + arvif->vif->type != NL80211_IFTYPE_MESH_POINT)) return -EINVAL; spin_lock_bh(&ar->data_lock);