Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git

ath.git patches for v5.19. Major changes:

ath11k

* enable keepalive during WoWLAN suspend

* implement remain-on-channel support
This commit is contained in:
Kalle Valo 2022-05-16 13:16:34 +03:00
commit d93185a929
17 changed files with 575 additions and 36 deletions

View File

@ -1233,6 +1233,7 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type)
{
const struct firmware *fw;
char boardname[100];
if (bd_ie_type == ATH10K_BD_IE_BOARD) {
if (!ar->hw_params.fw.board) {
@ -1240,9 +1241,19 @@ static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type)
return -EINVAL;
}
scnprintf(boardname, sizeof(boardname), "board-%s-%s.bin",
ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
ar->hw_params.fw.dir,
ar->hw_params.fw.board);
boardname);
if (IS_ERR(ar->normal_mode_fw.board)) {
fw = ath10k_fetch_fw_file(ar,
ar->hw_params.fw.dir,
ar->hw_params.fw.board);
ar->normal_mode_fw.board = fw;
}
if (IS_ERR(ar->normal_mode_fw.board))
return PTR_ERR(ar->normal_mode_fw.board);

View File

@ -2692,8 +2692,10 @@ static bool ath10k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta)
static enum wmi_phy_mode ath10k_mac_get_phymode_vht(struct ath10k *ar,
struct ieee80211_sta *sta)
{
struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) {
switch (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
return MODE_11AC_VHT160;
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
@ -6926,6 +6928,9 @@ static int ath10k_mac_validate_rate_mask(struct ath10k *ar,
struct ieee80211_sta *sta,
u32 rate_ctrl_flag, u8 nss)
{
struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
if (nss > sta->deflink.rx_nss) {
ath10k_warn(ar, "Invalid nss field, configured %u limit %u\n",
nss, sta->deflink.rx_nss);
@ -6933,19 +6938,19 @@ static int ath10k_mac_validate_rate_mask(struct ath10k *ar,
}
if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_VHT) {
if (!sta->deflink.vht_cap.vht_supported) {
if (!vht_cap->vht_supported) {
ath10k_warn(ar, "Invalid VHT rate for sta %pM\n",
sta->addr);
return -EINVAL;
}
} else if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HT) {
if (!sta->deflink.ht_cap.ht_supported || sta->deflink.vht_cap.vht_supported) {
if (!ht_cap->ht_supported || vht_cap->vht_supported) {
ath10k_warn(ar, "Invalid HT rate for sta %pM\n",
sta->addr);
return -EINVAL;
}
} else {
if (sta->deflink.ht_cap.ht_supported || sta->deflink.vht_cap.vht_supported)
if (ht_cap->ht_supported || vht_cap->vht_supported)
return -EINVAL;
}

View File

@ -9,6 +9,8 @@
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/dma-mapping.h>
#include <linux/of_address.h>
#include <linux/iommu.h>
#include "ahb.h"
#include "debug.h"
#include "hif.h"
@ -757,6 +759,172 @@ static int ath11k_ahb_setup_resources(struct ath11k_base *ab)
return 0;
}
static int ath11k_ahb_setup_msa_resources(struct ath11k_base *ab)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
struct device *dev = ab->dev;
struct device_node *node;
struct resource r;
int ret;
node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!node)
return -ENOENT;
ret = of_address_to_resource(node, 0, &r);
of_node_put(node);
if (ret) {
dev_err(dev, "failed to resolve msa fixed region\n");
return ret;
}
ab_ahb->fw.msa_paddr = r.start;
ab_ahb->fw.msa_size = resource_size(&r);
node = of_parse_phandle(dev->of_node, "memory-region", 1);
if (!node)
return -ENOENT;
ret = of_address_to_resource(node, 0, &r);
of_node_put(node);
if (ret) {
dev_err(dev, "failed to resolve ce fixed region\n");
return ret;
}
ab_ahb->fw.ce_paddr = r.start;
ab_ahb->fw.ce_size = resource_size(&r);
return 0;
}
static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
struct device *host_dev = ab->dev;
struct platform_device_info info = {0};
struct iommu_domain *iommu_dom;
struct platform_device *pdev;
struct device_node *node;
int ret;
/* Chipsets not requiring MSA need not initialize
* MSA resources, return success in such cases.
*/
if (!ab->hw_params.fixed_fw_mem)
return 0;
ret = ath11k_ahb_setup_msa_resources(ab);
if (ret) {
ath11k_err(ab, "failed to setup msa resources\n");
return ret;
}
node = of_get_child_by_name(host_dev->of_node, "wifi-firmware");
if (!node) {
ab_ahb->fw.use_tz = true;
return 0;
}
info.fwnode = &node->fwnode;
info.parent = host_dev;
info.name = node->name;
info.dma_mask = DMA_BIT_MASK(32);
pdev = platform_device_register_full(&info);
if (IS_ERR(pdev)) {
of_node_put(node);
return PTR_ERR(pdev);
}
ret = of_dma_configure(&pdev->dev, node, true);
if (ret) {
ath11k_err(ab, "dma configure fail: %d\n", ret);
goto err_unregister;
}
ab_ahb->fw.dev = &pdev->dev;
iommu_dom = iommu_domain_alloc(&platform_bus_type);
if (!iommu_dom) {
ath11k_err(ab, "failed to allocate iommu domain\n");
ret = -ENOMEM;
goto err_unregister;
}
ret = iommu_attach_device(iommu_dom, ab_ahb->fw.dev);
if (ret) {
ath11k_err(ab, "could not attach device: %d\n", ret);
goto err_iommu_free;
}
ret = iommu_map(iommu_dom, ab_ahb->fw.msa_paddr,
ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size,
IOMMU_READ | IOMMU_WRITE);
if (ret) {
ath11k_err(ab, "failed to map firmware region: %d\n", ret);
goto err_iommu_detach;
}
ret = iommu_map(iommu_dom, ab_ahb->fw.ce_paddr,
ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size,
IOMMU_READ | IOMMU_WRITE);
if (ret) {
ath11k_err(ab, "failed to map firmware CE region: %d\n", ret);
goto err_iommu_unmap;
}
ab_ahb->fw.use_tz = false;
ab_ahb->fw.iommu_domain = iommu_dom;
of_node_put(node);
return 0;
err_iommu_unmap:
iommu_unmap(iommu_dom, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size);
err_iommu_detach:
iommu_detach_device(iommu_dom, ab_ahb->fw.dev);
err_iommu_free:
iommu_domain_free(iommu_dom);
err_unregister:
platform_device_unregister(pdev);
of_node_put(node);
return ret;
}
static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
struct iommu_domain *iommu;
size_t unmapped_size;
if (ab_ahb->fw.use_tz)
return 0;
iommu = ab_ahb->fw.iommu_domain;
unmapped_size = iommu_unmap(iommu, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size);
if (unmapped_size != ab_ahb->fw.msa_size)
ath11k_err(ab, "failed to unmap firmware: %zu\n",
unmapped_size);
unmapped_size = iommu_unmap(iommu, ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size);
if (unmapped_size != ab_ahb->fw.ce_size)
ath11k_err(ab, "failed to unmap firmware CE memory: %zu\n",
unmapped_size);
iommu_detach_device(iommu, ab_ahb->fw.dev);
iommu_domain_free(iommu);
platform_device_unregister(to_platform_device(ab_ahb->fw.dev));
return 0;
}
static int ath11k_ahb_probe(struct platform_device *pdev)
{
struct ath11k_base *ab;
@ -816,10 +984,14 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
if (ret)
goto err_core_free;
ret = ath11k_hal_srng_init(ab);
ret = ath11k_ahb_fw_resources_init(ab);
if (ret)
goto err_core_free;
ret = ath11k_hal_srng_init(ab);
if (ret)
goto err_fw_deinit;
ret = ath11k_ce_alloc_pipes(ab);
if (ret) {
ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret);
@ -856,6 +1028,9 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
err_hal_srng_deinit:
ath11k_hal_srng_deinit(ab);
err_fw_deinit:
ath11k_ahb_fw_resource_deinit(ab);
err_core_free:
ath11k_core_free(ab);
platform_set_drvdata(pdev, NULL);
@ -891,6 +1066,7 @@ static int ath11k_ahb_remove(struct platform_device *pdev)
qmi_fail:
ath11k_ahb_free_irq(ab);
ath11k_hal_srng_deinit(ab);
ath11k_ahb_fw_resource_deinit(ab);
ath11k_ce_free_pipes(ab);
ath11k_core_free(ab);
platform_set_drvdata(pdev, NULL);

View File

@ -12,6 +12,15 @@ struct ath11k_base;
struct ath11k_ahb {
struct rproc *tgt_rproc;
struct {
struct device *dev;
struct iommu_domain *iommu_domain;
dma_addr_t msa_paddr;
u32 msa_size;
dma_addr_t ce_paddr;
u32 ce_size;
bool use_tz;
} fw;
};
static inline struct ath11k_ahb *ath11k_ahb_priv(struct ath11k_base *ab)

View File

@ -110,6 +110,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 0,
.ce_window_idx = 0,
.fixed_fw_mem = false,
.support_off_channel_tx = false,
},
{
.hw_rev = ATH11K_HW_IPQ6018_HW10,
@ -185,6 +186,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 0,
.ce_window_idx = 0,
.fixed_fw_mem = false,
.support_off_channel_tx = false,
},
{
.name = "qca6390 hw2.0",
@ -259,6 +261,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 0,
.ce_window_idx = 0,
.fixed_fw_mem = false,
.support_off_channel_tx = true,
},
{
.name = "qcn9074 hw1.0",
@ -333,6 +336,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 3,
.ce_window_idx = 2,
.fixed_fw_mem = false,
.support_off_channel_tx = false,
},
{
.name = "wcn6855 hw2.0",
@ -407,6 +411,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 0,
.ce_window_idx = 0,
.fixed_fw_mem = false,
.support_off_channel_tx = true,
},
{
.name = "wcn6855 hw2.1",
@ -480,6 +485,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 0,
.ce_window_idx = 0,
.fixed_fw_mem = false,
.support_off_channel_tx = true,
},
{
.name = "wcn6750 hw1.0",
@ -553,6 +559,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dp_window_idx = 1,
.ce_window_idx = 2,
.fixed_fw_mem = true,
.support_off_channel_tx = false,
},
};
@ -1620,9 +1627,11 @@ static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab)
ieee80211_stop_queues(ar->hw);
ath11k_mac_drain_tx(ar);
ar->state_11d = ATH11K_11D_IDLE;
complete(&ar->completed_11d_scan);
complete(&ar->scan.started);
complete(&ar->scan.completed);
complete(&ar->scan.on_channel);
complete(&ar->peer_assoc_done);
complete(&ar->peer_delete_done);
complete(&ar->install_key_done);
@ -1768,7 +1777,6 @@ static void ath11k_core_reset(struct work_struct *work)
ATH11K_RECOVER_START_TIMEOUT_HZ);
ath11k_hif_power_down(ab);
ath11k_qmi_free_resource(ab);
ath11k_hif_power_up(ab);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n");

View File

@ -121,7 +121,7 @@ struct ath11k_base;
#define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008
#define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c
#define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010
#define HAL_REO1_MISC_CTL 0x00000630
#define HAL_REO1_MISC_CTL(ab) ab->hw_params.regs->hal_reo1_misc_ctl
#define HAL_REO1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo1_ring_base_lsb
#define HAL_REO1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_reo1_ring_base_msb
#define HAL_REO1_RING_ID(ab) ab->hw_params.regs->hal_reo1_ring_id

View File

@ -771,10 +771,10 @@ static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab)
FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1);
ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val);
val = ath11k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTL);
val = ath11k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTL(ab));
val &= ~HAL_REO1_MISC_CTL_FRAGMENT_DST_RING;
val |= FIELD_PREP(HAL_REO1_MISC_CTL_FRAGMENT_DST_RING, HAL_SRNG_RING_ID_REO2SW1);
ath11k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTL, val);
ath11k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTL(ab), val);
ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab),
HAL_DEFAULT_REO_TIMEOUT_USEC);
@ -1983,6 +1983,9 @@ const struct ath11k_hw_regs ipq8074_regs = {
/* Shadow register area */
.hal_shadow_base_addr = 0x0,
/* REO misc control register, not used in IPQ8074 */
.hal_reo1_misc_ctl = 0x0,
};
const struct ath11k_hw_regs qca6390_regs = {
@ -2065,6 +2068,9 @@ const struct ath11k_hw_regs qca6390_regs = {
/* Shadow register area */
.hal_shadow_base_addr = 0x000008fc,
/* REO misc control register, not used in QCA6390 */
.hal_reo1_misc_ctl = 0x0,
};
const struct ath11k_hw_regs qcn9074_regs = {
@ -2147,6 +2153,9 @@ const struct ath11k_hw_regs qcn9074_regs = {
/* Shadow register area */
.hal_shadow_base_addr = 0x0,
/* REO misc control register, not used in QCN9074 */
.hal_reo1_misc_ctl = 0x0,
};
const struct ath11k_hw_regs wcn6855_regs = {
@ -2229,6 +2238,11 @@ const struct ath11k_hw_regs wcn6855_regs = {
/* Shadow register area */
.hal_shadow_base_addr = 0x000008fc,
/* REO misc control register, used for fragment
* destination ring config in WCN6855.
*/
.hal_reo1_misc_ctl = 0x00000630,
};
const struct ath11k_hw_regs wcn6750_regs = {
@ -2311,6 +2325,11 @@ const struct ath11k_hw_regs wcn6750_regs = {
/* Shadow register area */
.hal_shadow_base_addr = 0x00000504,
/* REO misc control register, used for fragment
* destination ring config in WCN6750.
*/
.hal_reo1_misc_ctl = 0x000005d8,
};
const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = {

View File

@ -204,6 +204,7 @@ struct ath11k_hw_params {
u8 dp_window_idx;
u8 ce_window_idx;
bool fixed_fw_mem;
bool support_off_channel_tx;
};
struct ath11k_hw_ops {
@ -379,6 +380,7 @@ struct ath11k_hw_regs {
u32 pcie_pcs_osc_dtct_config_base;
u32 hal_shadow_base_addr;
u32 hal_reo1_misc_ctl;
};
extern const struct ath11k_hw_regs ipq8074_regs;

View File

@ -1951,7 +1951,7 @@ static void ath11k_peer_assoc_h_vht(struct ath11k *ar,
/* Calculate peer NSS capability from VHT capabilities if STA
* supports VHT.
*/
for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) {
for (i = 0, max_nss = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
(2 * i) & 3;
@ -2272,7 +2272,7 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
/* Calculate peer NSS capability from HE capabilities if STA
* supports HE.
*/
for (i = 0, max_nss = 0, he_mcs = 0; i < NL80211_HE_NSS_MAX; i++) {
for (i = 0, max_nss = 0; i < NL80211_HE_NSS_MAX; i++) {
he_mcs = he_tx_mcs >> (2 * i) & 3;
/* In case of fixed rates, MCS Range in he_tx_mcs might have
@ -5551,8 +5551,8 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
}
arvif = ath11k_vif_to_arvif(skb_cb->vif);
if (ar->allocated_vdev_map & (1LL << arvif->vdev_id) &&
arvif->is_started) {
mutex_lock(&ar->conf_mutex);
if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) {
ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb);
if (ret) {
ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n",
@ -5570,6 +5570,7 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
arvif->is_started);
ath11k_mgmt_over_wmi_tx_drop(ar, skb);
}
mutex_unlock(&ar->conf_mutex);
}
}
@ -6155,6 +6156,11 @@ void ath11k_mac_11d_scan_stop(struct ath11k *ar)
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d vdev id %d\n",
ar->vdev_id_11d_scan);
if (ar->state_11d == ATH11K_11D_PREPARING) {
ar->state_11d = ATH11K_11D_IDLE;
complete(&ar->completed_11d_scan);
}
if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID) {
vdev_id = ar->vdev_id_11d_scan;
@ -7741,6 +7747,7 @@ ath11k_mac_validate_vht_he_fixed_rate_settings(struct ath11k *ar, enum nl80211_b
bool he_fixed_rate = false, vht_fixed_rate = false;
struct ath11k_peer *peer, *tmp;
const u16 *vht_mcs_mask, *he_mcs_mask;
struct ieee80211_link_sta *deflink;
u8 vht_nss, he_nss;
bool ret = true;
@ -7763,13 +7770,16 @@ ath11k_mac_validate_vht_he_fixed_rate_settings(struct ath11k *ar, enum nl80211_b
spin_lock_bh(&ar->ab->base_lock);
list_for_each_entry_safe(peer, tmp, &ar->ab->peers, list) {
if (peer->sta) {
if (vht_fixed_rate && (!peer->sta->deflink.vht_cap.vht_supported ||
peer->sta->deflink.rx_nss < vht_nss)) {
deflink = &peer->sta->deflink;
if (vht_fixed_rate && (!deflink->vht_cap.vht_supported ||
deflink->rx_nss < vht_nss)) {
ret = false;
goto out;
}
if (he_fixed_rate && (!peer->sta->deflink.he_cap.has_he ||
peer->sta->deflink.rx_nss < he_nss)) {
if (he_fixed_rate && (!deflink->he_cap.has_he ||
deflink->rx_nss < he_nss)) {
ret = false;
goto out;
}
@ -8345,6 +8355,118 @@ static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw,
return ret;
}
static int ath11k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct ath11k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
ar->scan.roc_notify = false;
spin_unlock_bh(&ar->data_lock);
ath11k_scan_abort(ar);
mutex_unlock(&ar->conf_mutex);
cancel_delayed_work_sync(&ar->scan.timeout);
return 0;
}
static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *chan,
int duration,
enum ieee80211_roc_type type)
{
struct ath11k *ar = hw->priv;
struct ath11k_vif *arvif = (void *)vif->drv_priv;
struct scan_req_params arg;
int ret;
u32 scan_time_msec;
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
switch (ar->scan.state) {
case ATH11K_SCAN_IDLE:
reinit_completion(&ar->scan.started);
reinit_completion(&ar->scan.completed);
reinit_completion(&ar->scan.on_channel);
ar->scan.state = ATH11K_SCAN_STARTING;
ar->scan.is_roc = true;
ar->scan.vdev_id = arvif->vdev_id;
ar->scan.roc_freq = chan->center_freq;
ar->scan.roc_notify = true;
ret = 0;
break;
case ATH11K_SCAN_STARTING:
case ATH11K_SCAN_RUNNING:
case ATH11K_SCAN_ABORTING:
ret = -EBUSY;
break;
}
spin_unlock_bh(&ar->data_lock);
if (ret)
goto exit;
scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2;
memset(&arg, 0, sizeof(arg));
ath11k_wmi_start_scan_init(ar, &arg);
arg.num_chan = 1;
arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list),
GFP_KERNEL);
if (!arg.chan_list) {
ret = -ENOMEM;
goto exit;
}
arg.vdev_id = arvif->vdev_id;
arg.scan_id = ATH11K_SCAN_ID;
arg.chan_list[0] = chan->center_freq;
arg.dwell_time_active = scan_time_msec;
arg.dwell_time_passive = scan_time_msec;
arg.max_scan_time = scan_time_msec;
arg.scan_flags |= WMI_SCAN_FLAG_PASSIVE;
arg.scan_flags |= WMI_SCAN_FILTER_PROBE_REQ;
arg.burst_duration = duration;
ret = ath11k_start_scan(ar, &arg);
if (ret) {
ath11k_warn(ar->ab, "failed to start roc scan: %d\n", ret);
spin_lock_bh(&ar->data_lock);
ar->scan.state = ATH11K_SCAN_IDLE;
spin_unlock_bh(&ar->data_lock);
goto free_chan_list;
}
ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ);
if (ret == 0) {
ath11k_warn(ar->ab, "failed to switch to channel for roc scan\n");
ret = ath11k_scan_stop(ar);
if (ret)
ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret);
ret = -ETIMEDOUT;
goto free_chan_list;
}
ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
msecs_to_jiffies(duration));
ret = 0;
free_chan_list:
kfree(arg.chan_list);
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct ieee80211_ops ath11k_ops = {
.tx = ath11k_mac_op_tx,
.start = ath11k_mac_op_start,
@ -8397,6 +8519,8 @@ static const struct ieee80211_ops ath11k_ops = {
#endif
.set_sar_specs = ath11k_mac_op_set_bios_sar_specs,
.remain_on_channel = ath11k_mac_op_remain_on_channel,
.cancel_remain_on_channel = ath11k_mac_op_cancel_remain_on_channel,
};
static void ath11k_mac_update_ch_list(struct ath11k *ar,
@ -8986,6 +9110,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
init_completion(&ar->bss_survey_done);
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
init_completion(&ar->scan.on_channel);
init_completion(&ar->thermal.wmi_sync);
INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
@ -9026,3 +9151,34 @@ void ath11k_mac_destroy(struct ath11k_base *ab)
pdev->ar = NULL;
}
}
int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
enum wmi_sta_keepalive_method method,
u32 interval)
{
struct ath11k *ar = arvif->ar;
struct wmi_sta_keepalive_arg arg = {};
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
return 0;
if (!test_bit(WMI_TLV_SERVICE_STA_KEEP_ALIVE, ar->ab->wmi_ab.svc_map))
return 0;
arg.vdev_id = arvif->vdev_id;
arg.enabled = 1;
arg.method = method;
arg.interval = interval;
ret = ath11k_wmi_sta_keepalive(ar, &arg);
if (ret) {
ath11k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
return 0;
}

View File

@ -8,6 +8,7 @@
#include <net/mac80211.h>
#include <net/cfg80211.h>
#include "wmi.h"
struct ath11k;
struct ath11k_base;
@ -173,4 +174,7 @@ void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb);
void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif);
int ath11k_mac_wait_tx_complete(struct ath11k *ar);
int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
enum wmi_sta_keepalive_method method,
u32 interval);
#endif

View File

@ -1970,6 +1970,21 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
for (i = 0; i < ab->qmi.mem_seg_count; i++) {
chunk = &ab->qmi.target_mem[i];
/* Firmware reloads in coldboot/firmware recovery.
* in such case, no need to allocate memory for FW again.
*/
if (chunk->vaddr) {
if (chunk->prev_type == chunk->type ||
chunk->prev_size == chunk->size)
continue;
/* cannot reuse the existing chunk */
dma_free_coherent(ab->dev, chunk->size,
chunk->vaddr, chunk->paddr);
chunk->vaddr = NULL;
}
chunk->vaddr = dma_alloc_coherent(ab->dev,
chunk->size,
&chunk->paddr,
@ -1990,6 +2005,8 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
chunk->type);
return -EINVAL;
}
chunk->prev_type = chunk->type;
chunk->prev_size = chunk->size;
}
return 0;
@ -2466,9 +2483,6 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab)
char path[100];
int ret;
if (m3_mem->vaddr || m3_mem->size)
return 0;
fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE);
if (IS_ERR(fw)) {
ret = PTR_ERR(fw);
@ -2478,6 +2492,9 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab)
return ret;
}
if (m3_mem->vaddr || m3_mem->size)
goto skip_m3_alloc;
m3_mem->vaddr = dma_alloc_coherent(ab->dev,
fw->size, &m3_mem->paddr,
GFP_KERNEL);
@ -2488,6 +2505,7 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab)
return -ENOMEM;
}
skip_m3_alloc:
memcpy(m3_mem->vaddr, fw->data, fw->size);
m3_mem->size = fw->size;
release_firmware(fw);

View File

@ -97,6 +97,8 @@ struct ath11k_qmi_event_msg {
struct target_mem_chunk {
u32 size;
u32 type;
u32 prev_size;
u32 prev_type;
dma_addr_t paddr;
u32 *vaddr;
void __iomem *iaddr;

View File

@ -139,6 +139,9 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
"reg hw scan wait left time %d\n", left);
}
if (ar->state == ATH11K_STATE_RESTARTING)
return 0;
bands = hw->wiphy->bands;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
if (!bands[band])

View File

@ -625,10 +625,25 @@ struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len)
return skb;
}
static u32 ath11k_wmi_mgmt_get_freq(struct ath11k *ar,
struct ieee80211_tx_info *info)
{
struct ath11k_base *ab = ar->ab;
u32 freq = 0;
if (ab->hw_params.support_off_channel_tx &&
ar->scan.is_roc &&
(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN))
freq = ar->scan.roc_freq;
return freq;
}
int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
struct sk_buff *frame)
{
struct ath11k_pdev_wmi *wmi = ar->wmi;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(frame);
struct wmi_mgmt_send_cmd *cmd;
struct wmi_tlv *frame_tlv;
struct sk_buff *skb;
@ -649,7 +664,7 @@ int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
cmd->vdev_id = vdev_id;
cmd->desc_id = buf_id;
cmd->chanfreq = 0;
cmd->chanfreq = ath11k_wmi_mgmt_get_freq(ar, info);
cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr);
cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr);
cmd->frame_len = frame->len;
@ -5264,6 +5279,8 @@ static void ath11k_wmi_event_scan_started(struct ath11k *ar)
break;
case ATH11K_SCAN_STARTING:
ar->scan.state = ATH11K_SCAN_RUNNING;
if (ar->scan.is_roc)
ieee80211_ready_on_channel(ar->hw);
complete(&ar->scan.started);
break;
}
@ -5346,6 +5363,8 @@ static void ath11k_wmi_event_scan_foreign_chan(struct ath11k *ar, u32 freq)
case ATH11K_SCAN_RUNNING:
case ATH11K_SCAN_ABORTING:
ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
if (ar->scan.is_roc && ar->scan.roc_freq == freq)
complete(&ar->scan.on_channel);
break;
}
}
@ -8959,3 +8978,44 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
}
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
const struct wmi_sta_keepalive_arg *arg)
{
struct ath11k_pdev_wmi *wmi = ar->wmi;
struct wmi_sta_keepalive_cmd *cmd;
struct wmi_sta_keepalive_arp_resp *arp;
struct sk_buff *skb;
size_t len;
len = sizeof(*cmd) + sizeof(*arp);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
if (!skb)
return -ENOMEM;
cmd = (struct wmi_sta_keepalive_cmd *)skb->data;
cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
WMI_TAG_STA_KEEPALIVE_CMD) |
FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
cmd->vdev_id = arg->vdev_id;
cmd->enabled = arg->enabled;
cmd->interval = arg->interval;
cmd->method = arg->method;
if (arg->method == WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE ||
arg->method == WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST) {
arp = (struct wmi_sta_keepalive_arp_resp *)(cmd + 1);
arp->tlv_header = FIELD_PREP(WMI_TLV_TAG,
WMI_TAG_STA_KEEPALVE_ARP_RESPONSE) |
FIELD_PREP(WMI_TLV_LEN, sizeof(*arp) - TLV_HDR_SIZE);
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);
}
ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
"wmi sta keepalive vdev %d enabled %d method %d interval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
}

View File

@ -5907,6 +5907,50 @@ struct wmi_pdev_set_geo_table_cmd {
u32 rsvd_len;
} __packed;
struct wmi_sta_keepalive_cmd {
u32 tlv_header;
u32 vdev_id;
u32 enabled;
/* WMI_STA_KEEPALIVE_METHOD_ */
u32 method;
/* in seconds */
u32 interval;
/* following this structure is the TLV for struct
* wmi_sta_keepalive_arp_resp
*/
} __packed;
struct wmi_sta_keepalive_arp_resp {
u32 tlv_header;
u32 src_ip4_addr;
u32 dest_ip4_addr;
struct wmi_mac_addr dest_mac_addr;
} __packed;
struct wmi_sta_keepalive_arg {
u32 vdev_id;
u32 enabled;
u32 method;
u32 interval;
u32 src_ip4_addr;
u32 dest_ip4_addr;
const u8 dest_mac_addr[ETH_ALEN];
};
enum wmi_sta_keepalive_method {
WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1,
WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2,
WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3,
WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4,
WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5,
};
#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
u32 cmd_id);
struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
@ -6087,5 +6131,7 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
struct ath11k_vif *arvif);
int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val);
int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar);
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
const struct wmi_sta_keepalive_arg *arg);
#endif

View File

@ -640,6 +640,24 @@ static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
return 0;
}
static int ath11k_wow_set_keepalive(struct ath11k *ar,
enum wmi_sta_keepalive_method method,
u32 interval)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_mac_vif_set_keepalive(arvif, method, interval);
if (ret)
return ret;
}
return 0;
}
int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
@ -691,6 +709,14 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
goto cleanup;
}
ret = ath11k_wow_set_keepalive(ar,
WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
WMI_STA_KEEPALIVE_INTERVAL_DEFAULT);
if (ret) {
ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret);
goto cleanup;
}
ret = ath11k_wow_enable(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
@ -786,6 +812,14 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
goto exit;
}
ret = ath11k_wow_set_keepalive(ar,
WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
WMI_STA_KEEPALIVE_INTERVAL_DISABLE);
if (ret) {
ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret);
goto exit;
}
exit:
if (ret) {
switch (ar->state) {

View File

@ -1391,19 +1391,6 @@ static int temp_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(temp);
/*---------freq------------*/
static int freq_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
u32 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
seq_printf(s, "Freq = %d\n", freq);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(freq);
/*---------link------------*/
static int link_show(struct seq_file *s, void *data)
{
@ -2380,7 +2367,6 @@ static const struct {
{"pmcdata", 0444, &fops_pmcdata},
{"pmcring", 0444, &fops_pmcring},
{"temp", 0444, &temp_fops},
{"freq", 0444, &freq_fops},
{"link", 0444, &link_fops},
{"info", 0444, &info_fops},
{"recovery", 0644, &fops_recovery},